JavaScript通过事件循环实现异步,宏任务(如setTimeout)和微任务(如Promise.then)交替执行;每次宏任务后清空微任务队列。例如同步代码执行完后,先处理微任务再执行下一个宏任务,async/await基于Promise,后续代码作为微任务执行,因此输出顺序为:script start、async1 start、async2、script end、async1 end、setTimeout。

JavaScript 是单线程语言,通过事件循环(Event Loop)实现异步操作的非阻塞执行。理解微任务与宏任务的执行机制,是掌握 JavaScript 异步编程的关键。
宏任务与微任务的基本概念
在 JavaScript 中,任务被分为两类:宏任务(Macro Task)和微任务(Micro Task)。
- 宏任务:包括整体代码块、setTimeout、setInterval、I/O 操作、UI 渲染等。
- 微任务:包括 Promise.then/catch/finally、MutationObserver、queueMicrotask 等。
每次事件循环中,先执行一个宏任务,然后清空所有可执行的微任务队列,再进入下一个宏任务。
事件循环的执行流程
事件循环按以下顺序运行:
立即学习“Java免费学习笔记(深入)”;
- 从宏任务队列中取出一个任务执行。
- 执行过程中遇到微任务,将其加入微任务队列。
- 当前宏任务执行完毕后,立即清空微任务队列中的所有任务。
- 更新 UI(如果需要),然后开始下一轮循环。
这意味着,微任务总是在当前宏任务结束后、下一个宏任务开始前被集中处理。
常见执行顺序示例解析
看下面这段代码:
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
}).then(() => {
console.log('promise2');
});
console.log('script end');
输出顺序为:
script startscript end
promise1
promise2
setTimeout
原因如下:
- 同步代码先执行,打印 'script start' 和 'script end'。
- setTimeout 加入宏任务队列。
- Promise 的 then 回调加入微任务队列。
- 当前宏任务(脚本)执行完后,立即执行所有微任务(两个 then)。
- 下一轮事件循环才执行 setTimeout 回调。
async/await 与微任务的关系
async 函数中的 await 后续代码本质上是 Promise.then 的语法糖,因此会被当作微任务处理。
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 startasync1 start
async2
script end
async1 end
因为 await async2() 后面的代码被包装成微任务,在本轮宏任务结束后执行。
基本上就这些。搞清楚宏任务和微任务的调度顺序,就能准确预测异步代码的执行结果。不复杂但容易忽略细节。










