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

在Firebase查询结果的forEach循环中正确使用async/await

解决forEach无法等待异步操作导致的Firebase数据查询问题

你遇到的核心问题是:forEach循环不会等待内部的异步函数执行完成。虽然你给forEach的回调加了async,但forEach本身不会处理这些回调返回的Promise,会直接继续执行后续代码,导致"USER TEST ADDED FOR GRAPH 3"先打印,而userTests数组还没填满就返回给前端了。

下面提供两种可靠的解决方法:

方法一:使用for...of循环(顺序执行)

for...of可以和await完美配合,会逐个等待每个异步操作完成后再进行下一次循环,保证执行顺序和数据填充的正确性:

async function getUserTestGraph(req, res) {
    const userGraphId = req.query.id;
    const userGraph = {};
    const userTests = [];

    const snapshot = await db.collection('userGraphs').where('id', '==', userGraphId).get();
    if (snapshot.empty) {
        console.log('No matching user Graph found');
        return res.status(200).json({ graphStatus: 200, message: "No user graph found!" });
    }

    const graphDoc = snapshot.docs[0].data();
    userGraph.id = graphDoc.id;
    userGraph.userId = graphDoc.userId;
    userGraph.diseaseName = graphDoc.diseaseName;
    userGraph.userDiseaseId = graphDoc.userDiseaseId;

    // 获取关联的测试列表
    const snapshotTests = await db.collection('userGraphTestSet').where('testSetId', '==', userGraphId).get();
    // 用for...of替代forEach
    for (const test of snapshotTests.docs) {
        const testData = test.data();
        const userTest = {
            id: testData.id,
            testSetId: testData.testSetId,
            userId: testData.userId,
            userTestId: testData.userTestId,
            testName: testData.testName
        };

        // 查询测试详细信息
        const snapshotTestInfo = await db.collection('userTests').where('id', '==', testData.userTestId).get();
        if (!snapshotTestInfo.empty) {
            const testInfo = snapshotTestInfo.docs[0].data();
            userTest.testAmount = testInfo.testAmount;
            userTest.testUnit = testInfo.testUnitId;
            userTest.testDate = testInfo.testDate;
        }

        console.log("USER TEST ADDED FOR GRAPH 1");
        console.log(userTest);
        userTests.push(userTest);
        console.log("USER TEST ADDED FOR GRAPH 2");
        console.log(userTests);
    }

    console.log("USER TEST ADDED FOR GRAPH 3");
    console.log(userTests);
    return res.status(200).json({ graphStatus: 200, userGraph: userGraph, userTests: userTests });
}

方法二:使用Promise.all + map(并行执行,效率更高)

如果不需要严格的顺序执行,用map把每个测试项的异步操作转换成Promise,再用Promise.all等待所有Promise完成,这样所有查询可以并行进行,提升性能:

async function getUserTestGraph(req, res) {
    const userGraphId = req.query.id;
    const userGraph = {};

    const snapshot = await db.collection('userGraphs').where('id', '==', userGraphId).get();
    if (snapshot.empty) {
        console.log('No matching user Graph found');
        return res.status(200).json({ graphStatus: 200, message: "No user graph found!" });
    }

    const graphDoc = snapshot.docs[0].data();
    userGraph.id = graphDoc.id;
    userGraph.userId = graphDoc.userId;
    userGraph.diseaseName = graphDoc.diseaseName;
    userGraph.userDiseaseId = graphDoc.userDiseaseId;

    // 获取关联的测试列表
    const snapshotTests = await db.collection('userGraphTestSet').where('testSetId', '==', userGraphId).get();
    // 用map生成所有异步操作的Promise数组
    const userTestPromises = snapshotTests.docs.map(async (test) => {
        const testData = test.data();
        const userTest = {
            id: testData.id,
            testSetId: testData.testSetId,
            userId: testData.userId,
            userTestId: testData.userTestId,
            testName: testData.testName
        };

        // 查询测试详细信息
        const snapshotTestInfo = await db.collection('userTests').where('id', '==', testData.userTestId).get();
        if (!snapshotTestInfo.empty) {
            const testInfo = snapshotTestInfo.docs[0].data();
            userTest.testAmount = testInfo.testAmount;
            userTest.testUnit = testInfo.testUnitId;
            userTest.testDate = testInfo.testDate;
        }

        console.log("USER TEST ADDED FOR GRAPH 1");
        console.log(userTest);
        return userTest;
    });

    // 等待所有Promise完成,得到完整的userTests数组
    const userTests = await Promise.all(userTestPromises);

    console.log("USER TEST ADDED FOR GRAPH 3");
    console.log(userTests);
    return res.status(200).json({ graphStatus: 200, userGraph: userGraph, userTests: userTests });
}

为什么forEach不行?

forEach的设计就是遍历执行回调,它不会关心回调是否是异步函数,也不会等待回调返回的Promise完成。你写的await snapshotTests.forEach(...)其实没有任何效果,因为forEach本身返回undefined,await undefined会直接resolve,不会等待内部的异步操作。

内容的提问来源于stack exchange,提问作者MattJ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:08:11