众所周知,JavaScript 是单线程
语言,只能同时执行做一件事(js只有一个线程,称之为main thread-主线程
)
1.Javascript 运行机制
main thread 主线程
和 call-stack 调用栈
(执行栈),所有的任务都会被放到调用栈
等待主线程执行
。
2.Javascript 任务
分为2类,同步任务
和异步任务
(异步又分为宏任务
和微任务
),同步和异步任务都是队列
,所以是先进先出
的。
3.执行顺序
,同步—>异步—>MicroTask(微任务)—>MacroTask(宏任务)
(异步包含宏任务和微任务,异步中微任务是优于宏任务执行的)
概念不懂,直接看下方!
JS 调用栈
JS调用栈采用的是后进先出
(数据结构)的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。
它用于存储正在执行的 js代码块及其运行环境。每当创建一个新的执行上下文,就会将其添加到调用栈中。这个执行上下文包含了该函数或代码块执行所需的所有变量、参数、作用域等。
MacroTask(宏任务)
宏任务通常包括整体代码块(script),setTimeout,setInterval,setImmediate,I/O 操作等异步操作,它们会被推入到宏任务队列中等待执行。当主线程执行完当前任务后,会去检查宏任务队列,如果队列中有任务,就会从队列中取出一个任务执行,直到队列为空。
MicroTask(微任务)
微任务通常包括 Promise 的回调函数,process.nextTick,MutationObserver 等异步操作,它们会被推入到微任务队列中等待执行。当一个宏任务执行完成后,会检查微任务队列,如果队列中有任务,就会依次取出任务执行,直到队列为空。注意,微任务的执行时机是在当前宏任务执行结束后,下一个宏任务开始之前,也就是说微任务的执行优先级高于宏任务。
同步和异步事件举例
举例1:
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('End');
上方代码输出顺序:
Start
End
Promise
Timeout
举例2:
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('End');
setTimeout(() => console.log('Timeout 2'), 0);
Promise.resolve().then(() => console.log('Promise 2'));
上方代码输出顺序:
Start
End
Promise
Promise 2
Timeout
Timeout 2
总结
总结一下,当 JavaScript 引擎执行代码时,先执行同步任务
,执行完同步任务后,再开始执行异步任
务,异步任务分宏任务
和微任务
,如果遇到了宏任务,会将它放到宏任务队列中等待执行;如果遇到了微任务,会将它放到微任务队列中等待执行。当主线程执行完当前任务后,会先执行微任务队列
中的任务,直到微任务队列为空,再去执行宏任务队列
中的任务,直到宏任务队列为空。这样就保证了异步任务的执行顺序和及时性,避免了可能出现的竞态条件和阻塞情况。
参考文章:
参考①:js微宏任务https://www.jb51.net/article/271092.htm
不足的地方请指教~