Map方法内嵌含async代码的while循环执行顺序问题
问题分析
你用Array.map()执行异步函数时,map会一次性创建并启动所有异步任务,所以数组里两个元素的循环会并行执行,自然达不到你要的串行输出效果。而Promise.all()只是等待所有并行任务完成,没法改变它们并行启动的本质,所以不管用没用它,输出顺序都不符合预期。
解决方案
要让数组元素的异步循环逐个串行执行,等前一个元素的所有循环步骤完成后再处理下一个,有两种简单可行的方式:
方案一:用for...of循环(最直观易读)
直接替换掉map,用for...of遍历数组,每次只处理一个元素,等它的异步循环完全结束后再进入下一次迭代:
async function sleep(time = 1) { const sleepMilliseconds = time * 1000 return new Promise(resolve => { setTimeout(() => { resolve(`Slept for: ${sleepMilliseconds}ms`) }, sleepMilliseconds) }) } async function main() { let a = [1,2] for (const el of a) { let i = 3 console.log(el,i) while(i>0) { console.log(el, i, 'main') console.log(el, i, await sleep(i)) i -= 1 } } } main()
方案二:用Array.reduce()串联Promise
如果喜欢链式写法,可以用reduce把每个元素的异步任务串联起来,确保前一个任务完成后再启动下一个:
async function sleep(time = 1) { const sleepMilliseconds = time * 1000 return new Promise(resolve => { setTimeout(() => { resolve(`Slept for: ${sleepMilliseconds}ms`) }, sleepMilliseconds) }) } async function main() { let a = [1,2] await a.reduce(async (prevTask, el) => { // 等待前一个元素的异步任务全部完成 await prevTask let i = 3 console.log(el,i) while(i>0) { console.log(el, i, 'main') console.log(el, i, await sleep(i)) i -= 1 } }, Promise.resolve()) // 初始Promise,确保第一个任务能正常启动 } main()
原代码失效的核心原因
Array.map()的特性是遍历数组时立刻执行传入的函数,所以你写的async(el)=>{...}会被一次性全部调用,所有异步循环同时启动,自然并行执行。Promise.all()接收的是已经启动的Promise数组,它只能等待所有任务结束,没法控制任务的启动顺序,所以用它也没法实现串行。
内容的提问来源于stack exchange,提问作者Kartik Setia
相关产品推荐
相关产品推荐

