关于事件循环,一道异步代码执行输出顺序问题
更多描述
setTimeout(() => {
console.log("A");
Promise.resolve().then(() => {
console.log("B");
});
}, 1000);
Promise.resolve().then(() => {
console.log("C");
});
new Promise((resolve) => {
console.log("D");
resolve("");
}).then(() => {
console.log("E");
});
async function sum(a, b) {
console.log("F");
}
async function asyncSum(a, b) {
await Promise.resolve();
console.log("G");
return Promise.resolve(a + b);
}
sum(3, 4);
asyncSum(3, 4);
console.log("H");
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 528
Author 回答者: shfshanyue
D F H C E G A B
Author 回答者: Echo-MakeGreatEffort
代码块的执行顺序:
-
同步代码块:
- JavaScript 引擎会先执行所有同步代码。
Promise.resolve()
是同步执行的,而Promise.then()
回调是微任务(microtask)。
-
宏任务和微任务:
- 宏任务(如
setTimeout
)会被放入任务队列,等到主线程空闲时才会执行。 - 微任务(如
Promise.then()
的回调)会在当前的同步代码执行完后立即执行。
- 宏任务(如
逐步分析代码:
-
Promise.resolve().then(() => { console.log(“C”); })
Promise.resolve()
立即被执行,但then()
回调被放入微任务队列。
-
new Promise((resolve) => { console.log(“D”); resolve(""); }).then(() => { console.log(“E”); })
- 构造函数中的代码是同步的,所以会立即执行,打印
"D"
。 resolve("")
立即被调用,then()
回调被放入微任务队列。
- 构造函数中的代码是同步的,所以会立即执行,打印
-
async function sum(a, b) { console.log(“F”); }
- 调用
sum(3, 4)
会立即执行函数中的同步代码,打印"F"
。
- 调用
-
async function asyncSum(a, b) { await Promise.resolve(); console.log(“G”); return Promise.resolve(a + b); }
- 调用
asyncSum(3, 4)
,await Promise.resolve()
会让出执行线程,console.log("G")
会作为微任务被执行。
- 调用
-
console.log(“H”)
- 这是同步代码,立即执行,打印
"H"
。
- 这是同步代码,立即执行,打印
-
微任务队列执行:
- 微任务队列中的顺序是:
console.log("C")
->console.log("E")
->console.log("G")
。
- 微任务队列中的顺序是:
-
setTimeout
- 宏任务在 1000ms 后执行,
setTimeout
回调会被放入任务队列,在执行时先打印"A"
,然后执行其中的微任务Promise.resolve().then(() => { console.log("B"); })
。
- 宏任务在 1000ms 后执行,
最终的输出顺序:
D
F
H
C
E
G
A
B
总结:
- 同步代码先执行,打印
"D"
,"F"
,"H"
。 - 然后执行微任务,打印
"C"
,"E"
,"G"
。 - 最后,宏任务中的代码会在 1000ms 后执行,打印
"A"
,随后执行其中的微任务,打印"B"
。