如何确保递归分页API调用在forEach循环完成后再执行后续代码
解决递归分页API调用的异步等待问题
原代码的核心问题
forEach不会等待异步操作完成,遍历过程中仅触发每个元素的异步逻辑,直接进入后续代码块。- 手动嵌套
new Promise属于Promise反模式,且递归时未正确返回Promise链,导致外层无法追踪递归的完成状态。 - 使用
JSON.parse(fieldResponse.body)不合适,fetch的response.body是ReadableStream,应使用response.json()异步解析JSON。
解决方案:用 Promise.all + 异步函数统一管理
方式1:用 async/await 简化递归逻辑
先封装单个元素的全量数据获取逻辑,再通过 map 收集所有异步任务,最后用 Promise.all 等待全部完成:
// 封装单个元素的分页请求逻辑 async function fetchAllForElement(element) { element.resultsArray = []; let startAt = 0; while (true) { // 发起分页请求 const fieldResponse = await fetch(`/api/request?startAt=${startAt}`); // 异步解析JSON响应 const results = await fieldResponse.json(); // 批量添加结果到数组 element.resultsArray.push(...results); // 判断是否是最后一页,是则终止循环 if (results.isLast) break; // 否则更新分页起始位置 startAt += 50; } return element; } // 收集所有元素的异步任务Promise const allFetchTasks = array.map(element => fetchAllForElement(element)); // 等待所有任务完成后执行后续代码 Promise.all(allFetchTasks) .then(processedElements => { console.log('所有分页请求已完成,数据收集完毕'); // --- 这里写你的后续代码块 --- }) .catch(error => { console.error('请求或处理过程中出错:', error); });
方式2:用Promise链式调用实现递归
如果不想用async/await,也可以用纯Promise链实现递归逻辑:
function fetchAllForElement(element) { element.resultsArray = []; const loop = (startAt) => { return fetch(`/api/request?startAt=${startAt}`) .then(fieldResponse => fieldResponse.json()) .then(results => { element.resultsArray.push(...results); // 最后一页返回元素,否则递归调用loop return results.isLast ? element : loop(startAt + 50); }) .catch(error => { console.error('分页请求出错:', error); throw error; // 抛出错误,让Promise.all能捕获 }); }; return loop(0); } // 同样用map+Promise.all等待所有任务完成 Promise.all(array.map(fetchAllForElement)) .then(() => { // --- 后续代码块 --- });
关键要点
- 用
map替代forEach:map会返回每个异步任务的Promise数组,方便用Promise.all统一等待。 Promise.all:会等待数组中所有Promise都resolve后才进入then回调,确保所有分页请求完成后再执行后续代码。- 避免Promise嵌套:直接利用
fetch返回的Promise进行链式调用或async/await,不要手动包裹多余的new Promise。 - 错误处理:在异步逻辑中抛出错误,让
Promise.all能捕获并处理,避免静默失败。
内容的提问来源于stack exchange,提问作者Quirk
相关产品推荐
相关产品推荐

