Promise.allSettled(結果全取得)の基本と実践
Promise.allSettled は「複数の非同期処理が、成功か失敗かに関係なくすべて終わるまで待って、各結果のステータスと値(または理由)をまとめて返す」ためのメソッドです。部分成功を扱いたい場面に向いています。
基本の書き方と返り値
const p1 = Promise.resolve(1);
const p2 = Promise.reject(new Error("oops"));
const p3 = new Promise(res => setTimeout(() => res(3), 100));
const results = await Promise.allSettled([p1, p2, p3]);
console.log(results);
/*
[
{ status: "fulfilled", value: 1 },
{ status: "rejected", reason: Error("oops") },
{ status: "fulfilled", value: 3 }
]
*/
JavaScript- 全件完了を待つ: 成否に関わらず「全部終わってから」配列で返す。
- 結果形式: それぞれが
{ status: "fulfilled", value }または{ status: "rejected", reason }。 - 順序: 入力配列の順序を維持。
直列 vs 並列の使い分け(イメージ)
- Promise.allSettled: 全ての完了を待ち、成功・失敗の内訳を確認して次の処理へ。
- Promise.all: 一つでも失敗したら即例外。全部成功前提で扱うとき向き。
- Promise.race/any: 最初に決着した一件(raceは成功/失敗どちらでも、anyは成功に限る)。
「部分成功を拾って続行したい」「失敗も記録してレポートしたい」なら allSettled が適任です。
すぐ使えるテンプレート集
1) 成功だけ抽出して使う
const settled = await Promise.allSettled([p1, p2, p3]);
const values = settled
.filter(r => r.status === "fulfilled")
.map(r => r.value);
console.log(values); // 成功した値だけ
JavaScript2) 失敗の理由をログにまとめる
const settled = await Promise.allSettled(tasks);
const errors = settled
.filter(r => r.status === "rejected")
.map(r => r.reason);
errors.forEach(e => console.error("失敗:", e));
JavaScript3) API群の部分成功を許容してUIを更新
async function loadDashboard() {
const [cfg, profile, stats] = await Promise.allSettled([
fetch("/config").then(r => r.json()),
fetch("/profile").then(r => r.json()),
fetch("/stats").then(r => r.json()),
]);
const data = {
config: cfg.status === "fulfilled" ? cfg.value : null,
profile: profile.status === "fulfilled" ? profile.value : null,
stats: stats.status === "fulfilled" ? stats.value : null,
};
const errors = [cfg, profile, stats]
.filter(x => x.status === "rejected")
.map(x => x.reason);
// 部分的に表示、失敗は通知
render(data);
if (errors.length) notify(errors.map(e => e.message).join("\n"));
}
JavaScript4) 失敗を握りつぶして継続(デフォルト値採用)
const settled = await Promise.allSettled([
fetchJSON("/a"),
fetchJSON("/b"),
fetchJSON("/c"),
]);
const safe = settled.map(r =>
r.status === "fulfilled" ? r.value : { ok: false, data: null }
);
JavaScript実務でのパターンとコツ
- 部分成功の集約: 一括ロードで一部失敗しても、取得できたものだけで画面を組み立てる。
- ログ・監視: 失敗理由をまとめてレポート、再試行の対象選定に使う。
- 非クリティカルの併走: メイン処理と関係しない補助タスク(統計送信、プレフェッチなど)を同時実行し、完了後に状態を集約。
- 後続処理の分岐: 成功値だけで次の段階へ、失敗はキューに入れてリトライ。
// リトライ対象を抽出して再実行
async function retryRejected(funcs, max = 2) {
let settled = await Promise.allSettled(funcs.map(f => f()));
for (let i = 0; i < max; i++) {
const indices = settled
.map((r, idx) => ({ r, idx }))
.filter(x => x.r.status === "rejected")
.map(x => x.idx);
if (!indices.length) break;
const retried = await Promise.allSettled(indices.map(j => funcs[j]()));
indices.forEach((j, k) => (settled[j] = retried[k]));
}
return settled;
}
JavaScriptよくある落とし穴と対策
- 「成功だけ返る」と誤認: allSettled は成功・失敗を混在で返す。必要に応じてフィルタして使う。
- エラーを見落とす: 例外としては投げられないため、失敗の確認を自分で行う必要がある。
- 対策:
status === "rejected"の分岐を必ず設け、監視・通知・再試行へつなげる。
- 対策:
- 大量並列の負荷: 失敗も含めて全件待つため、並列が多いと負担が大きい。
- 対策: バッチ化や同時数制限(並列キュー)を導入。
- 順序の勘違い: 返り値配列の順序は入力順。完了速度では並ばない。
- 対策: インデックスに意味を持たせ、順序前提の処理を書いておく。
練習問題(手を動かして覚える)
// 1) 成否混在の結果を確認
const out = await Promise.allSettled([
Promise.resolve("A"),
Promise.reject(new Error("X")),
new Promise(res => setTimeout(() => res("B"), 50)),
]);
console.log(out);
// 2) 成功だけ抽出して合計
const nums = await Promise.allSettled([
Promise.resolve(10),
Promise.reject(new Error("bad")),
Promise.resolve(5),
]);
const sum = nums
.filter(r => r.status === "fulfilled")
.reduce((acc, r) => acc + r.value, 0);
console.log(sum); // 15
// 3) 失敗の理由を一覧表示
const rs = await Promise.allSettled([
Promise.reject(new Error("network")),
Promise.resolve("ok"),
Promise.reject(new Error("timeout")),
]);
rs.filter(r => r.status === "rejected")
.forEach(r => console.log(r.reason.message));
// 4) デフォルト値で埋める
const filled = rs.map(r => r.status === "fulfilled" ? r.value : "N/A");
console.log(filled);
JavaScript直感的な指針
- 「全部終わってから、成功・失敗ごとに結果を確認したい」なら allSettled。
- 成功だけ使う・失敗はログやリトライへ—といった分岐を前提に設計。
- 大量並列は負荷に注意。必要なら同時数を絞る。
- 返り値の順序は入力どおり。インデックス前提で扱う。
