为什么async函数返回已决议的Promise时不会立刻完成兑现?
核心原理说明
你遇到的现象完全符合ECMAScript规范对async函数和Promise决议流程的定义,核心要先纠正两个常见认知误区:
- async函数永远返回自身创建的外层Promise,不会直接返回你内部return的Promise
MDN的描述只说明了普通返回值的情况,当你返回的是thenable(包括Promise实例)时,外层Promise不会直接复用你返回的实例,而是会启动「Promise解包流程」,等待你返回的Promise决议后,再用它的结果决议外层的Promise,这个解包过程需要多轮微任务调度。 - 用一个Promise作为参数调用另一个Promise的resolve方法时,需要2轮微任务才能完成决议
这是ES规范明确要求的逻辑:当执行resolve(p),且p是一个Promise实例时,引擎会先注册一个微任务调用p.then(),拿到p的结果后,还要再注册一个微任务才能完成外层Promise的决议。
第一个示例的差异解释
async function f1(){ return 100; } async function f2(){ return Promise.resolve(100); } console.log(f1()); // Promise {<fulfilled>: 100} console.log(f2()); // Promise <pending>
- f1返回普通值100,不需要解包,外层Promise直接被决议为100,所以同步打印时已经是fulfilled状态。
- f2返回已经决议的Promise,外层Promise需要启动解包流程,所以同步打印时还处于pending状态,解包完成后才会变成fulfilled。
你看到展开pending的Promise能看到[[PromiseResult]]是100,是浏览器控制台的实现特性:你展开时Promise已经完成解包,控制台会展示最新状态,不是打印时就已经决议完成。
测试代码的执行流程拆解
我们一步步分析你写的测试代码的完整执行顺序:
let o = new Promise(function (r){ r(100) }); o.then(x => console.log(p)) .then(x => console.log(p)) .then(x => console.log(p)) async function f(){ return new Promise(function (r){ r(100) }) } let p = f();
同步代码执行阶段
- 实例化Promise
o,立刻执行resolve(100),o变成fulfilled状态。 - 给o绑定第一个then回调,因为o已经fulfilled,第一个回调被推入微任务队列,此时微任务队列:
[第一个then回调] - 给第一个then返回的Promise绑定第二个then回调,此时第一个then的Promise还处于pending,第二个回调暂不进入队列。
- 给第二个then返回的Promise绑定第三个then回调,同样暂不进入队列。
- 执行
f():- 函数内部创建已经resolve(100)的Promise
innerP。 - async函数生成外层Promise
p,初始状态为pending。 - 启动解包流程:调用
p的resolve方法传入innerP,按照规范生成第一个解包微任务unpack1推入队列,此时微任务队列:[第一个then回调, unpack1]
- 函数内部创建已经resolve(100)的Promise
- 同步代码执行完毕,开始逐批清空微任务队列。
微任务第一轮处理
- 执行第一个then回调:打印p,此时p还没解包完成,输出
pending。回调返回undefined,第一个then的Promise被resolve,将第二个then回调推入队列,此时微任务队列:[unpack1, 第二个then回调] - 执行
unpack1:调用innerP.then(),因为innerP已经fulfilled,将第二个解包微任务unpack2推入队列,此时微任务队列:[第二个then回调, unpack2]
微任务第二轮处理
- 执行第二个then回调:打印p,
unpack2还没执行,p还是pending,输出pending。回调返回undefined,第二个then的Promise被resolve,将第三个then回调推入队列,此时微任务队列:[unpack2, 第三个then回调] - 执行
unpack2:用innerP的结果100决议p,p变成fulfilled状态,值为100。
微任务第三轮处理
- 执行第三个then回调:打印p,此时p已经决议完成,输出
Promise {<fulfilled>: 100}
这就完全解释了你观察到的「第三个then才打印出fulfilled状态」的现象。
关于await的补充说明
如果async函数内部有await关键字,逻辑完全一致:await会暂停函数执行,等后面的Promise决议后恢复执行,最终函数的返回值还是会走相同的解包流程,决议async函数自动生成的外层Promise。
内容的提问来源于stack exchange,提问作者imheretolearn1
相关产品推荐
相关产品推荐

