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函数里后续代码的执行,不会阻塞函数外部的同步代码。
具体执行步骤拆解:
- 先同步执行全局的
console.log("Calling foo...");,输出第一行内容。 - 调用
foo(),进入函数内部:- 定义
bar函数(此时不会执行)。 - 立即执行匿名async函数:
- 同步执行
console.log("Calling bar...");,输出第二行。 - 调用
bar(),返回一个处于pending状态的Promise(因为内部的setTimeout是宏任务,要等所有同步代码跑完才会触发)。 - 遇到
await,此时会暂停当前匿名async函数的执行,把console.log("After bar...");这段代码打包成Promise的回调任务,然后让出线程,继续执行foo函数之外的同步代码。
- 同步执行
- 定义
foo()执行完毕后,回到全局上下文,同步执行console.log("After foo...");,输出第三行。- 所有同步代码执行完成后,事件循环开始处理异步任务:
- 等待1ms后,setTimeout的宏任务触发,resolve之前的Promise,对应的回调任务(也就是
console.log("After bar...");)进入微任务队列。 - 事件循环处理微任务,执行这段代码,输出第四行。
- 等待1ms后,setTimeout的宏任务触发,resolve之前的Promise,对应的回调任务(也就是
关于Promise.then转换的正确性
你把await转成Promise.then的推理方式完全正确,因为async/await本质就是Promise的语法糖,两者的执行逻辑完全一致:
await bar()本质就是bar().then(res => { /* await之后的代码 */ })- 转换后的代码里,调用
bar()拿到Promise后注册.then()回调,此时匿名函数会直接执行完毕,让出线程给外部同步代码,这和await的行为完全匹配。
唯一的小细节是,原代码里await会把Promise的返回值赋值给res,你的转换代码没处理这个变量,但这对执行顺序的推理没有影响,核心逻辑是一致的。
内容的提问来源于stack exchange,提问作者Zoso
相关产品推荐
相关产品推荐

