为何throw Error会使async函数中被await的闭包同步执行?
问题解析:同步抛错与
await的执行顺序差异 要搞懂这三个示例的输出差异,核心是理解await处理同步执行代码时的两种分支逻辑:同步抛错 vs 正常返回值。
1. 示例1:传入异步函数的情况
当传入async () => { throw new Error() }时,这个异步函数本身会返回一个rejected状态的Promise。await遇到Promise时,会暂停当前函数执行,把后续的catch/finally逻辑放到微任务队列,随后让出执行权给当前同步栈。
执行顺序:
- 调用
load,执行到await closure(),closure返回rejected Promise,load的后续逻辑进入微任务 - 同步栈继续执行
console.log("hello") - 同步栈清空后,执行微任务队列中的
catch和finally,输出error和finished
最终输出顺序:hello → error → finished
2. 示例2:同步函数抛错的情况
当传入普通同步函数() => { throw new Error() }时,await closure()的执行逻辑发生了变化:
- 先同步执行closure函数,此时直接抛出错误——这个错误发生在
await进行Promise包装之前 - 错误立即触发
try块的catch分支,console.log("error")同步执行 - 接着
finally块也同步执行,输出finished - 等
load函数的同步执行部分全部完成后,才回到全局同步栈,执行console.log("hello")
这里的关键是:同步函数的抛错不会被包装成Promise的reject,而是直接在当前同步执行流程中触发捕获逻辑,完全没有异步调度的参与。
3. 示例3:同步函数正常返回的情况
当传入() => { return 1 }时,await closure()的逻辑和示例1一致:
- 同步执行closure函数,得到返回值1
await会把这个非Promise值包装成resolved状态的Promiseload的后续逻辑(仅finally)被放到微任务队列- 同步栈继续执行
console.log("hello") - 同步栈清空后执行微任务,输出
finished
最终输出顺序:hello → finished
总结
- 如果
await后面的表达式同步抛出错误,错误会在当前同步栈中直接被捕获,后续的catch/finally同步执行 - 如果
await后面的表达式返回Promise(异步函数或手动创建的),或者返回非Promise值(会被包装成resolved Promise),后续逻辑会进入微任务队列,等待同步栈清空后执行
内容的提问来源于stack exchange,提问作者Jose Carlos Ramírez
相关产品推荐
相关产品推荐

