为何forEach与for-of遍历异步调用时响应顺序及耗时不同?
JavaScript异步函数在循环中的执行逻辑疑问与解答
问题背景
代码片段
const sleep = async(delay, input) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(input); }, delay); }); }; const promises = [sleep(500, 1), sleep(1000, 3), sleep(750, 2)]; (async() => { console.time('runTime'); const res1 = []; const res2 = []; promises.forEach(async(promise) => { const res = await promise; res1.push(res); }); console.log('logging forEach loop response-1', res1); for (const promise of promises) { const res = await promise; res2.push(res); } console.log('logging forEach loop response-2', res1); console.log('logging for loop response', res2); console.timeEnd('runTime'); })();
执行输出
logging forEach loop response-1 [] logging forEach loop response-2 [ 1, 2, 3 ] logging for loop response [ 1, 3, 2 ] runTime: 1.011s
核心疑问
- 为何整个代码仅耗时约1秒?我理解
logging forEach loop response-1输出空数组是因为forEach的回调是异步的,但forEach本身并非异步,执行完后数组尚未填充;但for-of循环依次await每个Promise,按计算应耗时0.5+1+0.75=2.25秒,为何实际并非如此? - 为何
logging forEach loop response-2的输出按延迟顺序排列,而logging for loop response按原Promise数组顺序排列?我猜测forEach会创建独立的匿名回调实例,回调按延迟顺序resolve,但结合疑问1,仍无法理解for-of的耗时为何不是2.25秒。
解答
疑问1:总耗时为何约1秒?
核心原因是Promise的创建时机:当你定义promises数组时,三个sleep函数已经被调用,内部的setTimeout已经开始计时——三个Promise是同时启动的,不是等到for-of循环里的await才开始执行。
for-of循环中的await promise只是等待已经在运行的Promise完成,而非从头触发执行。三个Promise里最长的延迟是1秒,所以整个代码的总耗时由这个最长的Promise决定,最终约1秒,而非三个延迟时间相加的2.25秒。
疑问2:两个数组的顺序差异原因
- res1(forEach循环):forEach会立即遍历所有Promise,给每个Promise绑定独立的异步回调。每个回调中的
await promise会等对应Promise完成后,再把结果push进res1。三个Promise的完成顺序是:500ms的(值1)→750ms的(值2)→1000ms的(值3),所以res1最终是[1,2,3]。 - res2(for-of循环):for-of严格按照原数组的顺序处理每个Promise。哪怕某个Promise已经提前完成(比如第一个500ms的Promise),代码也会先await它并push结果1;接着处理第二个Promise(延迟1000ms),等它完成后push结果3;最后处理第三个Promise(延迟750ms,此时已经完成),push结果2。因此res2的顺序和原Promise数组一致,是
[1,3,2]。
内容的提问来源于stack exchange,提问作者pythoniesta
相关产品推荐
相关产品推荐

