如何在 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 的高效并发优势,又能精准掌控每个请求的生命周期与结果流——真正实现“并行发起、顺序处理、实时可见”。