症状
错误“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 按预期拒绝。
修复后补充一条回归测试,复现原始症状,防止问题再次出现。
这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。