You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Node.js中async/await执行顺序及事件循环交互机制的疑问

关于Node.js中async/await执行顺序及事件循环交互机制的疑问

咱们先把你的问题拆解透,核心是你对async/await和事件循环的交互逻辑有误解,一步步捋每个代码的执行过程,你就能明白为啥会出现那个输出顺序了。

先纠正一个核心误区

async函数不会一调用就被扔到事件队列里!它的执行逻辑是:调用后会同步执行函数体代码,直到遇到第一个await或者函数执行结束。只有await之后的代码,才会被放到微任务队列里,等当前主线程的同步代码全部执行完,再回来处理微任务。

分析你的第一个代码例子

先看你写的这段代码:

async function test2(){
    console.log('3 test2 is being executed');
}
async function test(){
    console.log('2 test is being executed');

    await test2();
    console.log('4 test 2 was awaited');
}

console.log('1 test is about to be called');
test();
console.log('5 test was called');

执行步骤全解析:

  1. 首先同步执行最外层的console.log('1 test is about to be called'); → 输出1 test is about to be called
  2. 调用test():因为是async函数,同步执行函数体直到遇到await:
    • 先执行console.log('2 test is being executed'); → 输出2 test is being executed
    • 接下来执行await test2():先调用test2(),同样test2是async函数,同步执行它的函数体:
      • 执行console.log('3 test2 is being executed'); → 输出3 test2 is being executed
      • test2没有异步操作,执行完毕后返回一个状态为resolved的Promise(所有async函数只要正常结束,都会默认返回resolved Promise)
    • 现在await这个resolved的Promise,此时会做两件事:
      • 把await后面的代码(也就是console.log('4 test 2 was awaited');)放到微任务队列
      • 暂停test函数的执行,让出主线程
  3. 主线程回到最外层,继续执行同步代码console.log('5 test was called'); → 输出5 test was called
  4. 现在主线程的同步代码全部执行完了,开始处理微任务队列里的任务:取出刚才放进去的console.log('4 test 2 was awaited');执行 → 输出4 test 2 was awaited

这就是为什么你最终得到的输出顺序是:

1 test is about to be called
2 test is being executed
3 test2 is being executed
5 test was called
4 test 2 was awaited

再看你编辑后的第二个例子

你后来加的这段代码:

async function test2(){
    console.log('3 test2 is being executed');
}
async function test(){
    console.log('2 test is being executed');

    await test2();
    console.log('4 test 2 was awaited');
}

console.log('1 test is about to be called');
let a=async ()=>{
    await test();
}
a();
console.log('5 test was called');

逻辑其实和第一个例子完全一致:

  1. 同步输出1 test is about to be called
  2. 调用a(),同步执行到await test():
    • 调用test(),同步执行到await test2():
      • 输出2 test is being executed
      • 调用test2()同步输出3 test2 is being executed,返回resolved Promise
      • await test2()把test里的console.log('4...')放到微任务队列,暂停test
    • await test()拿到的是test返回的resolved Promise,因为a函数里await之后没有代码,所以没有额外的微任务,但test里的微任务已经在队列里了
  3. 主线程继续同步输出5 test was called
  4. 处理微任务队列,执行console.log('4 test 2 was awaited');

所以输出顺序和第一个例子完全一样,本质还是await触发了微任务的添加,主线程优先跑完所有同步代码再处理微任务。

补充几个关键规则

  1. 没有await的async函数=同步函数:如果async函数里没有await,那它会完全同步执行,不会触发任何微任务,就像你说的去掉await后输出顺序是1 2 3 4 5,因为test函数会从头到尾同步执行完,再跑最外层的console.log(5)。
  2. await的本质是等待Promise状态:不管await的是async函数返回的Promise,还是手动创建的Promise,只要Promise是resolved状态,就会把后续代码扔到微任务队列;如果是pending状态,会等Promise变成resolved后再把后续代码扔到微任务队列。
  3. 微任务队列的优先级高于宏任务:微任务(比如await后续代码、Promise.then回调)会在主线程同步代码执行完后立即处理,而宏任务(比如setTimeout、setInterval)要等微任务队列清空后才会执行。

这样你就能完全明白为什么会出现这些执行顺序了,所有逻辑都是可预测的,核心就是抓住async函数的同步执行阶段,以及await对微任务队列的触发逻辑。

备注:内容来源于stack exchange,提问作者user2741831

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:33:03