如何在 Promise.all 完成后对每个已解析结果执行回调,并实时跟踪进度

12次阅读

如何在 Promise.all 完成后对每个已解析结果执行回调,并实时跟踪进度

本文介绍如何在 promise.all 全部成功完成后,安全、有序地为每个解析值执行自定义处理函数(如日志、数据转换或 dom 更新),同时通过监听单个 promise 的 .then() 实现精确的进度反馈。

Promise.all 本身不具备“逐个完成时触发回调”的能力——它只在所有 Promise 全部 resolve 后才统一返回一个包含全部结果的数组。因此,若想对每个已解析的响应单独执行逻辑(例如更新 ui 进度条、缓存中间结果或记录耗时),需采用“双层监听”策略:

  • 进度监听层:在调用 Promise.all 前,为每个原始 Promise 单独添加 .then(),用于实时统计已完成数量;
  • 结果处理层:在 Promise.all(…).then(results => {…}) 中遍历最终结果数组,对每个值执行业务逻辑(如 callbackResolve(res))。

以下是完整、可运行的实践示例:

// 示例回调函数:处理单个 fetch 响应 function callbackResolve(response) {   if (!response.ok) throw new Error(`HTTP ${response.status}`);   return response.json(); // 或其他转换逻辑 }  // 封装 fetch 并支持错误捕获(推荐) function fetchData(url) {   return fetch(url)     .catch(err => {       console.error(`Fetch failed for ${url}:`, err);       throw err; // 确保 Promise.all 能捕获失败     }); }  const urls = [   'https://jsonplaceholder.typicode.com/posts/1',   'https://jsonplaceholder.typicode.com/posts/2',   'https://jsonplaceholder.typicode.com/posts/3' ];  const promises = urls.map(fetchData);  // ? 步骤1:实时进度追踪(独立于 Promise.all) let resolvedCount = 0; promises.forEach((promise, index) => {   promise.then(() => {     resolvedCount++;     const progress = ((resolvedCount / promises.length) * 100).toFixed(1);     console.log(`✅ [Progress] ${resolvedCount}/${promises.length} (${progress}%)`);   }).catch(err => {     console.warn(`⚠️  Request ${index + 1} failed, but progress continues...`);   }); });  // ? 步骤2:统一结果处理(Promise.all 完成后执行) Promise.all(promises)   .then(responses => {     console.log('? All requests completed. Processing results...');     // 对每个响应执行 callbackResolve(支持链式操作)     return Promise.all(responses.map(callbackResolve));   })   .then(dataArray => {     console.log('? Final processed data:', dataArray);     // ✅ 此处可安全进行渲染、存储或后续聚合操作   })   .catch(error => {     console.error('❌ Promise.all rejected:', error);   });

关键注意事项:

  • ⚠️ 避免在 map(fetchData) 中直接 .then(callbackResolve):这会导致回调立即执行(因 fetch() 返回 Promise,.then() 立即注册但不等待),且 Promise.all 将接收 undefined 或非 Promise 值,破坏并行性;
  • ⚠️ 进度统计需用 let 变量 + 闭包:确保每个 .then() 共享同一计数器,而非使用 for…of 中未声明的变量(原文代码中 resolveProgres++ 存在拼写错误,已修正为 resolvedCount);
  • 推荐搭配 Promise.allSettled:若需容忍部分失败(如网络抖动),可用 Promise.allSettled 替代,并在 .then() 中过滤 status: ‘fulfilled’ 的项再处理;
  • 现代替代方案考虑 AbortController 或 Promise.race:对超时控制、取消请求等高级需求,应结合信号机制实现。

通过该模式,你既能享受 Promise.all 的高效并发优势,又能精准掌控每个请求的生命周期与结果流——真正实现“并行发起、顺序处理、实时可见”。

text=ZqhQzanResources