高级前端js【Q515】关于事件循环,一道异步代码执行输出顺序问题

关于事件循环,一道异步代码执行输出顺序问题

更多描述

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

代码块的执行顺序:

  1. 同步代码块

    • JavaScript 引擎会先执行所有同步代码。
    • Promise.resolve() 是同步执行的,而 Promise.then() 回调是微任务(microtask)。
  2. 宏任务和微任务

    • 宏任务(如 setTimeout)会被放入任务队列,等到主线程空闲时才会执行。
    • 微任务(如 Promise.then() 的回调)会在当前的同步代码执行完后立即执行。

逐步分析代码:

  1. Promise.resolve().then(() => { console.log(“C”); })

    • Promise.resolve() 立即被执行,但 then() 回调被放入微任务队列。
  2. new Promise((resolve) => { console.log(“D”); resolve(""); }).then(() => { console.log(“E”); })

    • 构造函数中的代码是同步的,所以会立即执行,打印 "D"
    • resolve("") 立即被调用,then() 回调被放入微任务队列。
  3. async function sum(a, b) { console.log(“F”); }

    • 调用 sum(3, 4) 会立即执行函数中的同步代码,打印 "F"
  4. 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") 会作为微任务被执行。
  5. console.log(“H”)

    • 这是同步代码,立即执行,打印 "H"
  6. 微任务队列执行

    • 微任务队列中的顺序是:console.log("C") -> console.log("E") -> console.log("G")
  7. setTimeout

    • 宏任务在 1000ms 后执行,setTimeout 回调会被放入任务队列,在执行时先打印 "A",然后执行其中的微任务 Promise.resolve().then(() => { console.log("B"); })

最终的输出顺序:

D
F
H
C
E
G
A
B

总结:

  • 同步代码先执行,打印 "D", "F", "H"
  • 然后执行微任务,打印 "C", "E", "G"
  • 最后,宏任务中的代码会在 1000ms 后执行,打印 "A",随后执行其中的微任务,打印 "B"