解説
Promise.allで一つ失敗したら全部止まる?allSettledと結果を比べよう
失敗したときの結果と、ほかの処理の実行を分ける作例。Promise.all・allSettledの順番、完了件数、キャンセルとの違いを確認。

複数の非同期処理をPromise.allで待っていたら、一つが失敗した。これで残りも中止されたのかな。Promise.allが拒否されることと、元の処理が中止されることは別だよ。
今回は通信をせず、短いタイマーで架空の三つの仕事を作る。Promiseの基本を知っている人向けに、結果が決まった時点と、全処理が終わった時点を比較しよう。
allは、一つが拒否されると拒否になる
Promise.allは、すべてが成功すれば結果を配列へまとめ、一つでも拒否されると拒否される。成功時の配列は、完了した順ではなく渡した順に対応するよ。
ただし、すでに開始した通信やタイマーを、allが自動的に取り消すわけではない。必要なら、対象の処理が対応する中止方法を別に設計する必要がある。
allSettledは、すべての成否を待つ
allSettledは、それぞれが成功または拒否のどちらかへ決まるまで待ち、statusとvalueまたはreasonを持つ結果の配列を返す。
こちらも配列の順番は入力に対応する。一番先に終わった処理が、必ず配列の先頭へ来るわけではないよ。
小さな例で、完了が続くことを見る
const completed = [];
function task(name, ms, fail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
completed.push(name);
if (fail) reject(new Error(name));
else resolve(name);
}, ms);
});
}
const jobs = [task("A", 30), task("B", 10, true), task("C", 50)];
try {
await Promise.all(jobs);
} catch {
console.log("all: rejected");
}
const results = await Promise.allSettled(jobs);
console.log(results.map(result => result.status));
console.log(completed.length);
この例ではall: rejectedのあと、fulfilled・rejected・fulfilledの三つの状態、完了件数3が出る。Bの失敗があっても、AとCのタイマーは実行されているんだ。
taskを呼んだ時点でタイマーを開始していることにも注目しよう。Promise.allへ関数を並べれば、関数自体を自動で呼んでくれるという例ではないよ。
作例では、結果時点と最終件数を分ける
- allを選び実行する。途中で拒否の結果が出ても、最後には三件とも完了することを見る。
- allSettledへ変え、三件の状態が入力順でそろうことを確認する。
- 完了ログの順番と、結果の配列の順番を比べる。
- 「最初から試す」で表示を初期化する。
作例のリセットは、この練習の表示を戻すもの。実際のサーバー処理を取り消したり、通信の失敗を元へ戻したりする仕組みではないよ。
一つでも失敗なら全体を失敗にするか
すべてそろわないと使えない結果なのか、成功した分だけでも扱いたいのかで、合う待ち方が変わる。allSettledにすれば、失敗が自動的に解決するわけでもない。
編集部ではNode.jsとChromiumで、三件の最終完了、状態の順番、途中の結果とリセットを確認した。実ネットワークや業務のキャンセル機構を試した結果ではないよ。イベントの一回限りの登録はonce、返り値の違いはmapとforEachも参考になるね。
動かしてみよう:拒否の結果と残りの処理
二つの待ち方を実行し、結果時点・最終件数・完了ログを比べよう。通信はしないよ。

みんなのコメント 0
感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。
最初のコメントをどうぞ。