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

JavaScript递归Fetch获取数据填充HTML时异步Promise处理问题

问题根因

Array.forEach 不支持等待异步回调执行完成,你传入的 async 回调返回的 Promise 没有被捕获等待,导致 CleanData 函数在所有嵌套的 API 请求完成前就提前返回了原始对象,此时数据还未被更新,所以读取到的是 undefined。

修正方案

将所有 forEach 遍历替换为支持异步等待的 for...of 循环,或者用 Promise.all 并行处理请求提升效率,这里提供两种实现方式:

方案1:串行请求(逻辑简单,适合请求量小的场景)

async function GetData(address) {
    const response = await fetch(address);
    // 可按需增加请求失败判断:if(!response.ok) throw new Error('接口请求失败')
    const json = await response.json();
    return json;
}
async function CleanData(origData) {
    // 替换forEach为支持异步等待的for...of循环
    for (const [key, value] of Object.entries(origData)) {
        // 处理数组内的链接字段
        if (Array.isArray(value)) {
            for (let i = 0; i < value.length; i++) {
                const element = value[i];
                if (typeof element === 'string' && element.indexOf("https") != -1) {
                    let newData = await GetData(element);
                    if (newData["name"] !== undefined) {
                        origData[key][i] = newData["name"]
                    } else if (newData["title"] !== undefined) {
                        origData[key][i] = newData["title"];
                    }
                }
            }
        }
        // 处理顶层的链接字段
        else if (typeof value === 'string' && value.indexOf("https") != -1) {
            let newData = await GetData(value);
            origData[key] = newData["name"];
        }
    }
    return origData;
}
function PopulateFields(fieldData) {
    nameId.innerText = fieldData["name"];
    valueIds.forEach(node => {
        const nodeId = node.id;
        node.innerText = fieldData[nodeId] ?? ''; // 增加兜底避免显示undefined
    });
}
document.getElementById("button").addEventListener("click", async () => {
    try {
        let data = await GetData(ApiUrl);
        data = await CleanData(data);           
        PopulateFields(data);
    } catch (e) {
        // 可按需增加用户可见的错误提示,比如alert('数据加载失败,请稍后重试')
        console.error('数据加载出错:', e)
    }
})

方案2:并行请求(速度更快,适合请求量大的场景)

如果不需要按顺序请求,可以用Promise.all同时发起所有嵌套请求,大幅减少总等待时间:

async function CleanData(origData) {
    const asyncTasks = []
    for (const [key, value] of Object.entries(origData)) {
        if (Array.isArray(value)) {
            for (let i = 0; i < value.length; i++) {
                const element = value[i];
                if (typeof element === 'string' && element.indexOf("https") != -1) {
                    // 把所有异步请求推入任务队列,同时记录要修改的字段位置
                    asyncTasks.push((async () => {
                        const newData = await GetData(element)
                        if (newData["name"] !== undefined) {
                            origData[key][i] = newData["name"]
                        } else if (newData["title"] !== undefined) {
                            origData[key][i] = newData["title"];
                        }
                    })())
                }
            }
        } else if (typeof value === 'string' && value.indexOf("https") != -1) {
            asyncTasks.push((async () => {
                const newData = await GetData(value);
                origData[key] = newData["name"];
            })())
        }
    }
    // 等待所有异步请求全部完成
    await Promise.all(asyncTasks)
    return origData;
}

额外优化建议

  • 可以给GetData增加请求失败的异常捕获逻辑,避免某个子接口挂掉导致整个数据处理流程中断
  • 替换字段内容时可以增加兜底值,避免接口返回没有name/title字段时页面显示undefined
  • 如果存在大量重复的URL请求,可以增加本地缓存逻辑减少重复请求,提升加载速度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 02:18:03