如何等待forEach内异步Rest-Api调用完成后再执行后续代码
问题根源
Array.prototype.forEach 不会等待传入的异步回调执行完成。当你给forEach传入async函数时,forEach会同步触发所有回调,直接跳过回调内部的await等待逻辑,立刻执行循环后面的代码,这就导致你写在循环后的FAIL!!!插入逻辑,会比循环内部的接口请求、DOM渲染更早执行。
可落地方案
方案1:并行执行(性能优先,无顺序要求时用)
用map替代forEach,收集每个异步回调返回的Promise对象,再用Promise.all等待所有Promise全部状态变更为完成后,再执行后续逻辑。
修正后代码:
async function fetchSmth(){ let response = await fetch('https://api.coindesk.com/v1/bpi/currentprice.json'); let json = await response.json(); // map会将每个async函数返回的Promise收集为数组 const asyncTasks = [json.bpi].map(async (element) => { if(element.USD.code == "USD"){ let response2 = await fetch('https://catfact.ninja/fact'); let json2 = await response2.json(); document.body.innerHTML = document.body.innerHTML + `<div id='a_1'>${json2.fact}</div>`; } }); // 阻塞等待所有循环内异步任务执行完毕 await Promise.all(asyncTasks); // 以下逻辑会在所有循环任务完成后才执行 document.body.innerHTML = document.body.innerHTML + `<div id='a_1'>FAIL!!!</div>`; } fetchSmth();
注意:该方案下循环内的多个异步任务是并行发起请求的,总耗时等于耗时最长的单个任务的耗时,执行效率更高
方案2:串行执行(有顺序要求时用)
如果需要严格按照数组顺序逐个执行循环内的异步逻辑,直接使用原生for...of循环即可,for循环内的await会在当前async函数的上下文内生效,等待当前异步任务执行完才会进入下一轮循环,循环全部结束后才会往后执行。
修正后代码:
async function fetchSmth(){ let response = await fetch('https://api.coindesk.com/v1/bpi/currentprice.json'); let json = await response.json(); // for...of循环内的await会正常阻塞 for (const element of [json.bpi]) { if(element.USD.code == "USD"){ let response2 = await fetch('https://catfact.ninja/fact'); let json2 = await response2.json(); document.body.innerHTML = document.body.innerHTML + `<div id='a_1'>${json2.fact}</div>`; } } // 以下逻辑会在所有循环任务完成后才执行 document.body.innerHTML = document.body.innerHTML + `<div id='a_1'>FAIL!!!</div>`; } fetchSmth();
注意:该方案下异步任务是按顺序串行执行的,总耗时等于所有任务耗时的总和,仅在有明确顺序依赖时使用
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

