Loading

ROUTE

ルートゼロの
アクティビティ

APIの一部が失敗したら?Promise.allとallSettledの選び方

3

JavaScriptの`Promise.all()`と`Promise.allSettled()`の違いは、途中で1件失敗したときの戻り方です。`Promise.all()`は入力のPromiseが1件でも拒否されると、その時点で返り値のPromiseも拒否されます。一方の`Promise.allSettled()`は、すべてが成功・失敗のどちらかに確定するまで待ち、それぞれの結果を返します。画面やAPIの処理で「全部必要」なら`all`、「部分成功を活かしたい」なら`allSettled`が基本です。

先に比較:全部成功が必要か、部分成功を残すか

比較する点 Promise.all() Promise.allSettled()
1件だけ失敗 返り値のPromiseが拒否される すべての完了後、各結果を確認できる
成功時に得る値 入力順の値の配列 入力順の結果オブジェクトの配列
失敗の情報 最初に拒否された理由をcatchで扱う 各要素のstatusとreasonで扱う
向いている処理 必要なデータが全部そろわないと画面を組めない 複数ウィジェットのうち取得できたものだけ表示する

どちらも複数の非同期処理をまとめる仕組みで、JavaScriptのスレッドを増やすAPIではありません。違うのは処理の成否をどう集約するかです。MDNの両リファレンスでも、依存関係のある処理には`all`、個別の結果を確認したい処理には`allSettled`が向くと説明されています。


Promise.all():1件失敗すると全体はrejectになる

ユーザープロフィールと権限の両方がそろって初めて表示できる画面なら、どちらかが欠けた時点で次の処理に進めない方が明確です。

async function loadRequiredData() {
  const [profile, permissions] = await Promise.all([
    fetchProfile(),
    fetchPermissions()
  ]);
  return { profile, permissions };
}

try {
  const data = await loadRequiredData();
  renderPage(data);
} catch (error) {
  showLoadError(error);
}

この例では`fetchProfile()`または`fetchPermissions()`が返すPromiseが拒否されると、`await Promise.all(…)`が例外になります。重要なのは、最初の失敗が他の通信を自動で中止するわけではないこと。`Promise.all()`の返り値は早くrejectされても、すでに始まった残りの処理は通常そのまま進みます。

用語解説:reject(拒否)
Promiseが成功値ではなく失敗理由で確定することです。`await`では通常、`try/catch`で失敗を受け取ります。


Promise.allSettled():失敗した処理だけ切り分ける

ニュース、通知、レコメンドなど独立した情報を並べる画面では、1件の取得失敗で全体を空白にする必要はありません。`allSettled`なら失敗箇所を区別し、成功した結果を使えます。

const results = await Promise.allSettled([
  fetchNews(),
  fetchNotifications(),
  fetchRecommendations()
]);

const names = ["news", "notifications", "recommendations"];
const viewData = {};

results.forEach((result, index) => {
  const key = names[index];
  if (result.status === "fulfilled") {
    viewData[key] = result.value;
  } else {
    reportWidgetError(key, result.reason);
    viewData[key] = null;
  }
});

renderWidgets(viewData);

結果は入力したPromiseと同じ順に並びます。成功時は`{ status: "fulfilled", value: … }`、失敗時は`{ status: "rejected", reason: … }`という形です。完了順に並び替えられるわけではありません。

ここで`status`を見ずに`result.value`だけを読み込むと、失敗を見落とします。失敗を許容する場合でも、ログ・再試行・代替表示のどれを行うかはアプリ側で決めます。


API通信ではHTTPエラーの判定も別に必要

`fetch()`はHTTP 404や500のレスポンスを受け取っただけでは通常rejectしません。`all`を使っても、HTTPステータスを検査しない限り「取得成功」のように扱ってしまうことがあります。API用の関数で`response.ok`を確認しましょう。

async function fetchJson(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${url}`);
  }
  return response.json();
}

const results = await Promise.allSettled([
  fetchJson("/api/news"),
  fetchJson("/api/notifications")
]);

ネットワーク障害とHTTPエラーを同じカテゴリにまとめるか、個別に表示するかも設計事項です。ユーザー向け画面に内部URLや詳細な例外をそのまま出さないように分けて扱います。

判断に迷ったときの実務チェック

  • どれか1件でも欠けたら画面の意味が崩れる? → `Promise.all()`を候補にする
  • 成功したものだけ表示しても価値がある? → `Promise.allSettled()`を候補にする
  • 失敗した処理を後から個別再試行したい? → `allSettled`の結果から失敗項目を抽出する
  • 途中で通信を止めたい? → `Promise.all()`任せにせず`AbortController`などキャンセル手段を別途設計する
  • 大量のリクエストを同時に投げる? → どちらも並列数を制限しないため、バッチ分割や同時実行数の制御を検討する

「速そうだから`all`」「安全そうだから`allSettled`」ではなく、失敗時に利用者へ何を返したいかで決めると、エラー処理が場当たり的になりません。

よくある誤解:失敗順・空配列・同時実行

疑問 実際の挙動
結果は完了した順に並ぶ? いいえ。両メソッドとも結果の並びは入力順です。
空配列を渡したら? どちらも空配列でfulfilledになります。
allSettledなら絶対に例外が起きない? 入力Promiseの個別rejectは結果として格納しますが、後続の処理や不正な引数など、別の例外まで防ぐ機能ではありません。
allのrejectで残りはキャンセルされる? いいえ。集約Promiseの拒否と元の処理の中止は別です。

「allSettledは失敗しない」と覚えるより、「それぞれの失敗を結果データとして受け取る」と理解すると誤用を避けられます。

非同期処理の基礎と関連するエラー処理

Promise自体や`async/await`の動きがまだ曖昧な場合は、集約メソッドの前に基礎を整理すると理解しやすくなります。

(非同期処理やPromiseの前提を確認したい場合については『非同期処理とは?JavaScriptとJavaの実例でわかる使い方・落とし穴・解決法』をご参照ください)

`Promise.try()`は、同期・非同期の関数呼び出しをPromiseとして扱いやすくするための別の手段です。`all`と`allSettled`の集約の違いとは目的が異なります。

(同期と非同期のエラー処理をまとめる話題を知りたい場合については『ES2025 Promise.try の使い方と活用例|同期・非同期のエラー処理を統一する方法』をご参照ください)

FAQ:Promiseの集約でよく聞かれること

Promise.all()で1件失敗した場合、残りの成功結果は取れますか?

戻り値のPromiseは拒否されるため、すべての成功値を配列で受け取る通常の書き方では取得できません。部分結果を必要とするならallSettledを使うか、個別の失敗をcatchして値に変換する方法を検討します。

Promise.allSettled()の結果で失敗だけ抽出できますか?

はい。`results.filter(result => result.status === "rejected")`で失敗結果を抽出し、`reason`を確認できます。元の処理名も必要なら、入力順との対応を保持します。

allSettled()はall()より必ず遅いですか?

必ずしもそうではありません。all()は最初の拒否を早く返しますが、allSettled()は全件が確定するまで待ちます。入力の処理自体が速くなるわけではなく、どの時点で結果を返す必要があるかが違います。

まとめ:エラーを全体として扱うか、個別に扱うか

  • `Promise.all()`:全件成功が必要な処理で使う。1件の拒否で集約Promiseも拒否される。
  • `Promise.allSettled()`:部分成功を活かしたい処理で使う。全件の成否を結果として受け取る。
  • どちらも元の処理を自動キャンセルしない。HTTPエラー判定、再試行、同時実行数の制御は必要に応じて別に設計する。

手元のAPI呼び出しで「1件失敗したら画面を止めるべきか」を先に書き出し、その答えに合わせて`all`と`allSettled`を選んでみてください。

カジュアル面談はこちら

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。