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
相关产品推荐
相关产品推荐

