Axios异步请求致数组输出乱序及响应提前发送问题求助
问题描述
编写了两个Express路由接口:
/currentReleases:通过Axios GET请求获取Minecraft 1.12及之后的正式版本,功能正常;/availablePaperReleases:遍历上述版本调用PaperMC的API,但存在两个问题:- forEach循环内的异步GET请求导致
console.log输出顺序完全随机; - "Sending data"日志会在所有请求未完成时提前执行,响应也被提前发送。
- forEach循环内的异步GET请求导致
相关代码
正常工作的/currentReleases路由
router.get("/currentReleases", (req, res) => { get("https://launchermeta.mojang.com/mc/game/version_manifest.json").then((data) => { var currentReleases = Array() data.versions.forEach(version => { const releaseDate = new Date(version.releaseTime); const releaseDate_1_12 = new Date("2017-06-02T13:50:27+00:00") if(releaseDate_1_12 <= releaseDate) { if(version.type == "release") { currentReleases.push(version) } } }); res.send(currentReleases); }) });
存在问题的/availablePaperReleases路由
router.get("/availablePaperReleases", (req,res) => { get("http://localhost:3001/currentReleases").then((data) => { var currentReleases = Array(); data.forEach((paper) => { currentReleases.push(paper.id); }) var currentPaperBuilds = Array(); currentReleases.forEach((version, index, array) => { // 顺序正确 // console.log(version + " : " + index + " : " + array.length) get("https://api.papermc.io/v2/projects/paper/versions/"+version+"/builds").then((paperdata) => { // 顺序随机 console.log(version + " : " + index + " : " + array.length) }).catch(error => console.log("no data")) if(index == array.length - 1) { console.log("Sending data"); res.send(currentPaperBuilds); } }) }); });
get方法代码
async function get(url) { let response = await axios.get(url); return response.data }
问题原因
- forEach不等待异步操作:
forEach是同步循环,会直接走完所有迭代,不会等待内部的异步get请求完成。所以当循环到最后一个元素时,直接执行了res.send,此时大部分异步请求还未返回结果。 - 异步请求响应速度不一致:每个PaperMC API请求的响应时间不同,先返回的请求会先触发
then里的console.log,导致输出顺序随机。
解决方案
方案1:使用for...of循环(串行执行,保证请求顺序)
把forEach替换为for...of,配合await等待每个异步请求完成,既能保证请求和输出顺序一致,也能确保所有请求完成后再发送响应。
修改后的代码:
router.get("/availablePaperReleases", async (req,res) => { try { const data = await get("http://localhost:3001/currentReleases"); const currentReleases = data.map(paper => paper.id); const currentPaperBuilds = []; for (const version of currentReleases) { try { const paperdata = await get(`https://api.papermc.io/v2/projects/paper/versions/${version}/builds`); console.log(`${version} : ${currentReleases.indexOf(version)} : ${currentReleases.length}`); currentPaperBuilds.push(paperdata); // 根据业务需求处理返回数据 } catch (error) { console.log("no data"); } } console.log("Sending data"); res.send(currentPaperBuilds); } catch (error) { res.status(500).send("Failed to fetch releases"); } });
方案2:使用Promise.all(并行执行,效率更高)
如果不需要严格按顺序发起请求,只要求最终结果顺序和原数组一致,用Promise.all可以并行发起所有请求,大幅提升效率。
修改后的代码:
router.get("/availablePaperReleases", async (req,res) => { try { const data = await get("http://localhost:3001/currentReleases"); const currentReleases = data.map(paper => paper.id); // 生成所有请求的Promise数组 const promises = currentReleases.map(async (version, index) => { try { const paperdata = await get(`https://api.papermc.io/v2/projects/paper/versions/${version}/builds`); console.log(`${version} : ${index} : ${currentReleases.length}`); return paperdata; // 返回需要的数据 } catch (error) { console.log("no data"); return null; // 可根据需求设置请求失败的默认值 } }); // 等待所有Promise执行完成 const currentPaperBuilds = await Promise.all(promises); console.log("Sending data"); res.send(currentPaperBuilds.filter(item => item !== null)); // 过滤掉请求失败的项 } catch (error) { res.status(500).send("Failed to fetch releases"); } });
内容的提问来源于stack exchange,提问作者LinuxSquare
相关产品推荐
相关产品推荐

