关于事件循环,一道异步代码执行输出顺序问题
更多描述
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"。