JS面试题:如何改造代码使所有setTimeout执行完后再输出Completed
问题原因分析
原代码的setTimeout是异步非阻塞API,调用后会直接进入事件循环等待触发,不会阻塞主线程执行。因此循环调用callMe后主线程会立即执行console.log('Completed'),等到对应延迟时间到了才会依次打印三次Called,和预期输出顺序不符。
改造方案
按照提示使用await关键字改造,需要做两处核心调整:
- 改造
callMe函数,让它返回Promise实例,await只能等待Promise状态变更后才会继续执行后续代码 - 将循环和最终打印逻辑包裹在
async作用域内(await必须在async标记的作用域中使用)
改造后的完整代码如下:
// 改造callMe返回Promise function callMe(delay) { return new Promise(resolve => { setTimeout(() => { console.log('Called'); resolve(); // 异步逻辑执行完成后变更Promise状态 }, delay); }) } const delays = [500, 1000, 1500]; // 用async立即执行函数包裹异步逻辑 (async () => { for (let delay of delays) { await callMe(delay); // 等待当前callMe执行完成再进入下一次循环 } console.log('Completed'); })();
如果运行环境支持顶层await(比如ES模块、现代浏览器、Node.js 14.8+版本),可以省略立即执行函数,直接写:
for (let delay of delays) { await callMe(delay); } console.log('Completed');
可选优化:并发执行减少总耗时
上面的写法是串行执行三个异步调用,总耗时为500+1000+1500=3000ms,如果希望三个调用并发执行,总耗时只需要1500ms,可以用Promise.all实现:
(async () => { await Promise.all(delays.map(delay => callMe(delay))); console.log('Completed'); })();
内容的提问来源于stack exchange,提问作者Nishant
相关产品推荐
相关产品推荐

