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

async IIFE中await执行行为疑问:为何未按预期阻塞?

关于async/await执行顺序的疑问与解答

问题场景

为理解async/await的执行行为,编写了以下JavaScript代码:

function foo() {
  async function bar() {
    return new Promise((resolve)=> {
      setTimeout(()=> {
        resolve("Done");
      }, 1);
    });
  }

  (async function () {
    console.log("Calling bar...");
    const res = await bar();
    console.log("After bar...");
  })();
}
console.log("Calling foo...");
foo();
console.log("After foo...");

预期输出:

Calling foo...
Calling bar...
After bar...
After foo...

实际输出:

Calling foo...
Calling bar...
After foo...
After bar...

疑问点:

  • 为何脚本未在const res = await bar();处等待,反而执行了console.log("After foo...");?
  • async匿名代码块内的await是否会导致async代码块被延迟?
  • 将代码转换为基于Promise.then的形式来推理是否正确?是否遗漏了关键点?

核心原因:await只阻塞当前async函数内部

JavaScript是单线程语言,await的作用范围仅限当前所在的async函数内部,它只会暂停这个async函数里后续代码的执行,不会阻塞函数外部的同步代码。

具体执行步骤拆解:

  1. 先同步执行全局的console.log("Calling foo...");,输出第一行内容。
  2. 调用foo(),进入函数内部:
    • 定义bar函数(此时不会执行)。
    • 立即执行匿名async函数:
      • 同步执行console.log("Calling bar...");,输出第二行。
      • 调用bar(),返回一个处于pending状态的Promise(因为内部的setTimeout是宏任务,要等所有同步代码跑完才会触发)。
      • 遇到await,此时会暂停当前匿名async函数的执行,把console.log("After bar...");这段代码打包成Promise的回调任务,然后让出线程,继续执行foo函数之外的同步代码。
  3. foo()执行完毕后,回到全局上下文,同步执行console.log("After foo...");,输出第三行。
  4. 所有同步代码执行完成后,事件循环开始处理异步任务:
    • 等待1ms后,setTimeout的宏任务触发,resolve之前的Promise,对应的回调任务(也就是console.log("After bar...");)进入微任务队列。
    • 事件循环处理微任务,执行这段代码,输出第四行。

关于Promise.then转换的正确性

你把await转成Promise.then的推理方式完全正确,因为async/await本质就是Promise的语法糖,两者的执行逻辑完全一致:

  • await bar()本质就是bar().then(res => { /* await之后的代码 */ })
  • 转换后的代码里,调用bar()拿到Promise后注册.then()回调,此时匿名函数会直接执行完毕,让出线程给外部同步代码,这和await的行为完全匹配。

唯一的小细节是,原代码里await会把Promise的返回值赋值给res,你的转换代码没处理这个变量,但这对执行顺序的推理没有影响,核心逻辑是一致的。

内容的提问来源于stack exchange,提问作者Zoso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 03:48:35