You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Axios异步请求致数组输出乱序及响应提前发送问题求助

问题描述

编写了两个Express路由接口:

  • /currentReleases:通过Axios GET请求获取Minecraft 1.12及之后的正式版本,功能正常;
  • /availablePaperReleases:遍历上述版本调用PaperMC的API,但存在两个问题:
    1. forEach循环内的异步GET请求导致console.log输出顺序完全随机;
    2. "Sending data"日志会在所有请求未完成时提前执行,响应也被提前发送。

相关代码

正常工作的/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
}

问题原因

  1. forEach不等待异步操作:forEach是同步循环,会直接走完所有迭代,不会等待内部的异步get请求完成。所以当循环到最后一个元素时,直接执行了res.send,此时大部分异步请求还未返回结果。
  2. 异步请求响应速度不一致:每个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 10:18:18