宏任务先执行,微任务在宏任务后立即清空队列,async/await中await后续代码作为微任务执行,setTimeout属于宏任务延后执行。

JavaScript 是单线程语言,通过事件循环(Event Loop)实现异步操作的管理。理解微任务与宏任务的执行机制,是掌握 JS 异步编程的关键。
宏任务与微任务的基本概念
宏任务(Macrotask):每次事件循环中执行的一个完整任务单元,比如 script 全局代码、setTimeout、setInterval、I/O、UI 渲染等。
微任务(Microtask):在当前任务结束后、下一轮事件循环开始前立即执行的任务,比如 Promise.then、MutationObserver、queueMicrotask 等。
事件循环的流程是:执行一个宏任务 → 执行所有可执行的微任务 → 进入下一个宏任务。
立即学习“Java免费学习笔记(深入)”;
执行顺序:微任务优先于下一个宏任务
每当一个宏任务执行完毕,JS 引擎会清空当前所有的微任务队列,再进入下一个宏任务。
这意味着即使微任务是在宏任务中间创建的,也会在该宏任务结束后立刻执行。例如:
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
输出顺序为:1 → 4 → 3 → 2。
- 全局 script 是第一个宏任务
- 同步代码先输出 1 和 4
- Promise 的 then 回调进入微任务队列,宏任务结束后执行,输出 3
- setTimeout 属于宏任务,进入下一轮事件循环,最后输出 2
async/await 与微任务的关系
async 函数中的 await 后续代码会被包装成微任务。
看这个例子:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
async1();
console.log('script end');
输出为:script start → async1 start → async2 → script end → async1 end。
- async1 执行输出 'async1 start'
- await 调用 async2,其内部同步代码执行,输出 'async2'
- await 后面的代码被包装成微任务,所以 'async1 end' 在当前宏任务结束后才执行
常见误区与注意事项
很多人误以为 setTimeout(fn, 0) 会立即执行,实际上它只是将回调加入下一个宏任务队列,必须等待当前所有微任务完成。
微任务执行时机非常“紧迫”,适合用于需要尽快响应的操作,如 Promise 链式调用、状态变更通知等。
避免在微任务中无限递归,比如连续调用 queueMicrotask,可能导致主线程阻塞,无法进行 UI 渲染或其他异步操作。
基本上就这些。搞清楚宏任务和微任务的层级关系,就能准确预测 JS 代码的执行顺序。不复杂但容易忽略细节。










