Appearance
5. 事件循环
难度等级:🟡 中级 分类:JavaScript 归档日期:2026-10-20
JavaScript 中的事件循环,是协调同步代码和异步任务、决定它们什么时候执行的一套机制。
不同的宿主环境会有一些区别,下面以浏览器为例。
宏任务(Task):执行整个脚本、setTimeout、setInterval、用户点击等事件的回调、postMessage、XHR 的 load 回调等。
微任务(Microtask):
Promise的.then()、.catch()、.finally()中的回调;queueMicrotask(callback);MutationObserver监听到符合条件的 DOM 变更时的回调;await后面的代码。
执行机制
- 执行一个宏任务,运行其中的同步代码;
- 执行过程中遇到微任务,加入微任务队列;遇到宏任务,加入宏任务队列;
- 当前宏任务执行完,依次执行微任务队列中的所有微任务;
- 微任务队列清空后,执行下一个宏任务。
javascript
let resolveP;
const p = new Promise(resolve => {
resolveP = resolve;
});
p.then(() => {
console.log('A'); // 只是注册了回调,此时 p 还是 pending,没有加入微任务队列
});
Promise.resolve().then(() => {
console.log('B');
});
resolveP(); // 此时 A 的回调才加入微任务队列
queueMicrotask(() => {
console.log('C');
});
console.log('D');
// 打印结果:D B A C互动演示
可以用 ← → 方向键或按钮逐步查看调用栈和任务队列的变化。
演示:链式 then
链式 then 与 queueMicrotask
1Promise.resolve()
2 .then(() => { // MA
3 console.log('A');
4 queueMicrotask(() => { // MB
5 console.log('B');
6 });
7 })
8 .then(() => { // MC
9 console.log('C');
10 });
11queueMicrotask(() => { // MD
12 console.log('D');
13});
14console.log('E');
正确答案:EADBC
执行任务:整段脚本
调用栈(正在执行)
整段脚本控制台输出
空微任务队列
空挂起等待(依附的 Promise 未完成,尚未入队)
空Promise.resolve() 得到一个已经 fulfilled 的 Promise,记作 p1。
1 / 13
演示:宏任务与微任务的嵌套
宏任务与微任务的嵌套
1console.log('1')
2setTimeout(() => { // T1
3 console.log('2')
4 Promise.resolve().then(() => console.log('3')) // M3
5})
6Promise.resolve().then(() => { // M1
7 console.log('4')
8 setTimeout(() => console.log('5')) // T2
9 Promise.resolve().then(() => console.log('6')) // M2
10})
11console.log('7')
第 1 轮 · 整段脚本
调用栈(正在执行)
整段脚本控制台输出
空微任务队列
空任务队列(宏任务)
空整段脚本作为第一个宏任务入栈。
1 / 13
演示:两条 then 链
两条 then 链 + 嵌套 queueMicrotask + setTimeout
1console.log('A');
2setTimeout(() => { // T1
3 console.log('B');
4 Promise.resolve().then(() => { // MC
5 console.log('C');
6 });
7}, 0);
8Promise.resolve() // p1
9 .then(() => { // MD,返回 p2
10 console.log('D');
11 queueMicrotask(() => { // ME
12 console.log('E');
13 });
14 })
15 .then(() => { // MF,挂在 p2
16 console.log('F');
17 });
18Promise.resolve() // p3
19 .then(() => { // MG,返回 p4
20 console.log('G');
21 })
22 .then(() => { // MH,挂在 p4
23 console.log('H');
24 });
25queueMicrotask(() => { // MI
26 console.log('I');
27 queueMicrotask(() => { // MJ
28 console.log('J');
29 });
30});
31console.log('K');
正确答案:AKDGIEFHJBC
第 1 轮 · 执行任务:整段脚本
调用栈(正在执行)
整段脚本控制台输出
空微任务队列
空任务队列(宏任务)
空挂起等待(依附的 Promise 未完成,尚未入队)
空整段脚本作为第一个宏任务入栈。这题有两条 then 链、一个嵌套的 queueMicrotask,还有一个定时器。
1 / 26
演示:await 的 tick 数
await 的 tick 数:await 普通值 vs await 返回 Promise 的 async 函数
1console.log('A')
2setTimeout(() => console.log('B'), 0) // T1
3async function f1() {
4 console.log('C')
5 await f2() // f2 返回的 Promise 记作 pf2
6 console.log('D') // f1续
7}
8async function f2() {
9 console.log('E')
10 return Promise.resolve() // 注意:返回的是 Promise
11}
12async function f3() {
13 await null
14 console.log('F') // f3续1
15 await null
16 console.log('G') // f3续2
17}
18f1()
19f3()
20Promise.resolve()
21 .then(() => console.log('H')) // MH
22 .then(() => console.log('I')) // MI
23 .then(() => console.log('J')) // MJ
24console.log('K')
正确答案:ACEKFHGIDJB
第 1 轮 · 执行任务:整段脚本
调用栈(正在执行)
整段脚本控制台输出
空微任务队列
空任务队列(宏任务)
空挂起等待(依附的 Promise 未完成,尚未入队)
空整段脚本入栈。三个 async 函数声明只是定义,不执行。
1 / 22