为何获取async函数返回的Promise结果必须使用await
现象复现代码
const delay = ms => new Promise(res => setTimeout(res, ms)); async function notInstant() { await delay(1); return 1; } (async function() { let a = notInstant(); console.log(a); await delay(10000); console.log("Still a promise after 10 seconds even though the function only waits for 1 millisecond =>", a); a = await a; console.log("Printing directly after but now is resolved with the correct value", a); })();
底层原理解释
出现这个反直觉的现象,本质是对两个JS核心规则理解有偏差:
- 所有async函数的返回值固定是Promise对象
执行let a = notInstant()时,a里存储的从一开始就是一个Promise实例的引用,赋值完成后这个引用不会自动发生变化。哪怕这个Promise在1ms后就从pending状态变为fulfilled、内部已经存好了resolve值1,它本身依然是Promise类型的对象,不会凭空转换成数字1。
你等待10秒后打印a,输出的还是最开始存在变量里的Promise引用,自然看到的是Promise对象——只不过这时候这个Promise早就完成了状态落定,只是你没有主动把内部存储的值取出来而已。 - await的核心作用是提取Promise的落定值,而非触发Promise执行
不少人对await的误解是"加了await才会等待Promise执行完成",实际上在你调用notInstant()的那一刻,函数内部的逻辑就已经开始执行了,完全不需要等你写await才启动。
await真正的运行逻辑是:暂停当前async函数的执行,让出事件循环线程,等后面跟随的Promise状态落定后,把Promise内部存储的resolve(或reject)值取出来,作为整个await表达式的返回值。
你执行a = await a时,a指向的那个Promise早在近10秒前就已经resolve完成,所以这里不会产生任何额外等待,会直接把内部存的值1取出来重新赋值给a。这时候变量a存储的内容才从原来的Promise引用变成了数字1,打印自然能看到正确结果。
关键误区:Promise状态从pending变为fulfilled/rejected时,只会修改自身内部的状态和值属性,不会自动替换掉变量里存储的对象引用。你不主动通过
.then()回调或者await把内部值提取出来,变量里永远存的是Promise对象,和它有没有resolve、resolve了多久没有任何关系。
打个通俗的比方:你点了份外卖,骑手10分钟就把餐送到你家门口挂着了(对应Promise在1ms后完成resolve,值已经存在Promise对象内部),你在家打了10小时游戏没开门取餐(对应10秒内没有对a做任何取值操作),外卖不会自己拆开跑到你餐桌上。等你开门把外卖拿进来拆开(对应执行await a),你才能拿到里面的餐食,这时候你桌上放的才是能直接吃的饭,不是外面的外卖包装盒。
内容的提问来源于stack exchange,提问作者Sante Kyaku
相关产品推荐
相关产品推荐

