JavaScript 事件循环机制详解:宏任务、微任务与执行顺序
Introduction
JavaScript 是单线程语言,靠事件循环(Event Loop)实现异步操作。理解事件循环、微任务队列(Microtask Queue)和宏任务队列(Macrotask Queue)的区别与执行顺序,是解决异步代码调试问题的根本,也是高级 JavaScript 开发者的必备知识。
任务队列与事件循环
/* 事件循环的工作流程:
1. 执行同步代码(调用栈)
2. 执行所有微任务(微任务队列)
3. 执行一个宏任务(宏任务队列)
4. 重复 2-3 */
console.log('1 - 同步');
setTimeout(function() {
console.log('4 - setTimeout 宏任务');
}, 0);
Promise.resolve()
.then(function() {
console.log('3 - Promise.then 微任务');
});
console.log('2 - 同步');
// 输出顺序:1 → 2 → 3 → 4
宏任务 vs 微任务
/* 宏任务(Macrotask):每一轮事件循环只执行一个 */
setTimeout(function() { console.log('timeout'); }, 0);
setInterval(function() { console.log('interval'); }, 0);
requestAnimationFrame(function() { console.log('rAF'); });
I/O 操作、UI 渲染
/* 微任务(Microtask):在当前宏任务结束后立即执行 */
Promise.then() / Promise.catch()
queueMicrotask()
MutationObserver
/* 执行顺序演示 */
setTimeout(function() { console.log('macro'); }, 0);
Promise.resolve().then(function() { console.log('micro1'); });
Promise.resolve().then(function() { console.log('micro2'); });
// micro1 → micro2 → macro(两个微任务在一个宏任务前全部清空)
async/await 与微任务
/* async 函数中的 await 会产出微任务 */
async function demo() {
console.log('1');
await Promise.resolve();
console.log('2'); // 这行代码在微任务中执行
}
console.log('0');
demo();
console.log('3');
// 输出:0 → 1 → 3 → 2
/* await 之后的代码 = 微任务 */
async function demo2() {
console.log('A');
await fetch('/api/data'); // 等待微任务
console.log('B'); // 微任务中执行
}
demo2();
console.log('C');
// A → C → B
实战:理解代码执行顺序
console.log('1 - 全局脚本');
setTimeout(function() { console.log('4 - 宏任务1'); }, 0);
setTimeout(function() { console.log('7 - 宏任务2'); }, 0);
Promise.resolve().then(function() { console.log('3 - 微任务1'); });
Promise.resolve().then(function() {
console.log('5 - 微任务2');
setTimeout(function() { console.log('6 - 嵌套宏任务'); }, 0);
});
console.log('2 - 全局脚本');
// 最终输出:1 → 2 → 3 → 5 → 4 → 6 → 7
常见问题
- 为什么微任务先于宏任务:微任务在当前宏任务结束后、下一个宏任务开始前全部清空,保证高优先级任务(Promise等)能尽快执行。
- async/await 是 Promise 的语法糖:await 后面的代码实际上在 Promise.then() 微任务中执行。
- Vue :利用微任务队列,在 DOM 更新后执行回调。
- 浏览器渲染:渲染属于宏任务,在每轮事件循环的末尾执行,所以大量微任务可能阻塞渲染。
- 事件循环可视化:loupe
- MDN 事件循环:MDN Event Loop