JavaScript 事件循环机制详解:宏任务、微任务与执行顺序

小飞兽 JavaScript 239 次阅读 2026-07-21

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