← 中文资源

简体中文版本

Promise.all 首个失败即拒绝 — 部分结果丢失

通过按所需失败策略在 Promise.all、allSettled、any 之间选择,避免一次拒绝就丢弃全部有用结果。

阅读英文原文

症状

错误“Promise.all 首个失败即拒绝 — 部分结果丢失”指向一个明确的失效模式。下面依次给出症状、定位该原因的检查命令、根本原因、修复方案,以及证明修复有效的验证步骤。

可观察到的症状包括:

  • Promise.all 拒绝后其余成功结果不可得
  • 单个失败请求使整批中止
  • 需要同时处理逐项成功与失败

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

node -e "Promise.all([Promise.resolve(1), Promise.reject(new Error('x'))]).catch(e => console.log(e.message))"

all 以首个错误拒绝,已解析的值 1 不会上报。

步骤 2

node -e "Promise.allSettled([Promise.resolve(1), Promise.reject(new Error('x'))]).then(r => console.log(r))"

allSettled 永不拒绝,逐项报告 status 及 value 或 reason。

常见诱因(按出现频率排序):

  • 在应容忍逐项失败时使用了 Promise.all
  • 未对单个 promise 加 catch 以转换拒绝
  • 期望“首个成功”却用了 all

根因

Promise.all 在任一输入拒绝时立即拒绝并丢弃其余结果。Promise.allSettled 等待全部并报告每个结果;Promise.any 在首个成功时解析,仅当全部失败才拒绝。

修复

按意图选择组合器:全部必需用 all,允许部分成功用 allSettled,首个成功用 any,首个完成(可配超时)用 race。

注意:allSettled 把错误处理交给你;忘记检查被拒绝项会静默丢弃失败。

// tolerate partial failures
const results = await Promise.allSettled(urls.map((u) => fetch(u)));
const ok = results.filter((r) => r.status === 'fulfilled').map((r) => r.value);
const failed = results.filter((r) => r.status === 'rejected').map((r) => r.reason);

// all: fail fast when every result is required
const all = await Promise.all(urls.map((u) => fetch(u)));

// any: first success wins
const first = await Promise.any(urls.map((u) => fetch(u)));

验证

用一项失败的批处理运行,确认 allSettled 给出成功值与一条 rejected 记录,而 all 按预期拒绝。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。