JavaScript 任务队列详解:Event Loop、宏任务与微任务
在 JavaScript 的世界里,异步编程是一个至关重要的概念。JavaScript 采用 单线程 运行方式,但能够处理异步任务,这一切都要归功于 事件循环(Event Loop) 机制。本文将深入剖析 JavaScript 的 任务队列(Task Queue),包括 宏任务(Macrotask) 和 微任务(Microtask),并结合示例解析代码的执行顺序。
1. 事件循环(Event Loop)
Event Loop
是 JavaScript 运行时的核心机制,负责调度 同步任务 和 异步任务。
执行顺序:
- 执行 同步代码(同步代码会直接进入调用栈
Call Stack
运行)。 - 执行 微任务队列(Microtask Queue) 里的所有任务。
- 执行 宏任务队列(Macrotask Queue) 里的一个任务。
- 回到 第 2 步,检查是否有新的 微任务,如果有,则执行所有微任务。
- 循环往复,直到所有任务执行完成。
2. Web APIs 与异步任务
在 JavaScript 代码执行过程中,遇到 异步任务(如 setTimeout
、Promise
、fetch
请求等)时,并不会阻塞主线程,而是将其交给 Web APIs 处理。等任务完成后,相应的回调函数会被放入 任务队列,等待执行。
常见的 Web APIs 任务:
- 定时器(
setTimeout
、setInterval
) - 网络请求(
fetch
、XMLHttpRequest
) - DOM 事件(
click
、mousemove
) - I/O 操作(Node.js)
这些任务完成后,会按照一定的优先级放入 任务队列,由 事件循环 调度执行。
3. 宏任务(Macrotask) vs. 微任务(Microtask)
JavaScript 的 任务队列 分为 宏任务队列 和 微任务队列,它们的主要区别如下:
3.1 宏任务(Macrotask)
- 宏任务(Macrotask) 是相对 微任务(Microtask) 而言的较大单位任务。
- 常见的宏任务包括:
setTimeout
setInterval
setImmediate
(Node.js)requestAnimationFrame
I/O
操作(如fs.readFile
)- 事件回调(如
click
、mousemove
)
- 执行顺序: 每次执行一个 宏任务 之后,会立即执行 所有微任务。
3.2 微任务(Microtask)
- 微任务(Microtask) 是比宏任务优先级更高的异步任务。
- 常见的微任务包括:
Promise.then
MutationObserver
queueMicrotask
process.nextTick
(Node.js 专有)
- 执行顺序: 每次执行完同步代码或宏任务后,都会立即执行 所有 微任务。
4. 执行顺序示例解析
来看一个经典的示例,分析 JavaScript 任务队列的执行顺序:
console.log("script start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve().then(() => {
console.log("promise1");
}).then(() => {
console.log("promise2");
});
console.log("script end");
执行流程解析
- 同步代码 依次执行:
console.log("script start")
✅console.log("script end")
✅
- ``** 回调** 进入 宏任务队列。
- ``** 回调** 进入 微任务队列。
- 同步代码执行完毕后,执行微任务队列:
console.log("promise1")
✅console.log("promise2")
✅
- 执行宏任务队列:
console.log("setTimeout")
✅
最终输出结果
script start
script end
promise1
promise2
setTimeout
5. 另一个复杂案例解析
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
return Promise.resolve("D");
}).then((msg) => {
console.log(msg);
});
console.log("E");
执行流程
- 同步代码执行:
A
、E
- ``** 进入宏任务队列**
- ``** 进入微任务队列**
- 执行微任务:
C
D
- 执行宏任务:
B
最终输出结果
A
E
C
D
B
6. 总结
Event Loop 执行顺序
- 执行同步代码(主线程上的任务)。
- 执行所有微任务(Microtask Queue)。
- 执行一个宏任务(Macrotask Queue)。
- 回到步骤 2,执行所有新的微任务。
- 继续执行宏任务队列中的下一个任务。
优先级对比
- 微任务 > 宏任务(微任务会在每个宏任务前全部执行完)。
- 多个微任务 会按照添加顺序执行。
- 多个宏任务 也是按照添加顺序执行。
掌握 事件循环(Event Loop) 的工作原理,有助于优化 JavaScript 代码的执行流程,避免常见的异步问题,提高程序的执行效率。
希望这篇文章能帮助你更好地理解 JavaScript 的 任务队列机制!