将Promise重构为Async/Await时为何出现undefined?
咱们先直接定位问题根源:你重构后的longtime函数没有返回Promise,这导致await longtime()根本无法等待异步操作完成——因为await只能正确等待Promise对象,要是你给它一个非Promise值(比如这里的undefined,因为longtime没写return语句),它会直接把这个值包装成已resolve的Promise,相当于没等异步操作结束就继续执行后面的代码了,自然会打乱串行逻辑,甚至出现undefined相关的异常。
先理清楚原Promise代码为什么能串行
原代码里的one函数返回的Promise,会一直等到内部嵌套的Promise(包裹setTimeout的那个)执行完成后,才调用rs()来resolve自身,所以每次one().then(_ => one())都能保证前一个one完全执行完毕,才启动下一个,完美实现串行效果。
修复重构后的代码
我们只需要两步就能搞定:
第一步:修复longtime函数,让它返回Promise
async function one() { console.log('start'); await longtime(); console.log('endLong'); } // 核心:让longtime返回Promise,才能被await正确等待 function longtime() { return new Promise((resolve) => { setTimeout(() => { console.log('after 3 sec'); resolve(); // 异步操作完成后,手动resolve这个Promise }, 3000); }); }
小提示:
longtime其实不需要加async关键字,因为它已经直接返回Promise了——加了也不报错,但没必要,反而手动返回Promise的逻辑更直观。
第二步:用Async/Await实现串行调用
原代码用.then()链式调用实现串行,换成Async/Await的话,我们可以写一个专门的执行函数,依次await每个one():
async function runSequence() { await one(); await one(); await one(); } runSequence();
执行这段代码后,就会和原Promise代码的效果完全一致,依次输出:
start after 3 sec endLong start after 3 sec endLong start after 3 sec endLong
补充:为什么之前会出现undefined?
因为longtime没有返回值,默认返回undefined,await undefined会立即resolve,所以console.log('endLong')会在setTimeout的回调执行前就运行,同时one函数返回的Promise也会提前resolve,导致下一个one()提前启动,打乱了串行逻辑;另外在某些场景下,await undefined还可能触发和undefined相关的警告或错误。
内容的提问来源于stack exchange,提问作者Shakirov Ramil

