JavaScript | Promise.all + finally の複合制御

JavaScript JavaScript
スポンサーリンク

ここは「複数の非同期処理(fetch など)をまとめて実行して、最後に共通の後処理をする」
という実務で非常に役立つテクニックです。

以下で、

  • 悪い例(落とし穴あり)
  • 良い例(安全で読みやすい制御)
    を対比しながら、Promise.allfinally の組み合わせ方を解説します。

シナリオ

3つの API を並列で呼び出す:

/api/user
/api/posts
/api/comments

どれも時間がかかるが、「ローディング中スピナー」を出しておきたい。
→ すべて終わったらスピナーを消す(成功でも失敗でも)。


❌ 悪い例:finally を忘れて重複コードに

async function loadAllDataBad() {
  showLoadingSpinner(); // ローディング表示

  try {
    const [user, posts, comments] = await Promise.all([
      fetch("/api/user").then(res => res.json()),
      fetch("/api/posts").then(res => res.json()),
      fetch("/api/comments").then(res => res.json())
    ]);
    console.log("すべて成功", { user, posts, comments });
  } catch (err) {
    console.error("どれか失敗しました", err);
  }

  // ← finally がないので、ここで後処理を書く必要がある
  hideLoadingSpinner(); // 成功・失敗どちらでも呼ばれる保証がない
}
JavaScript

問題点:

  • 途中で例外が起きると、hideLoadingSpinner() が呼ばれない可能性がある。
  • つまり「ローディング中のまま止まる」バグが起きやすい。

✅ 良い例:finally で共通後処理を保証する

async function loadAllDataGood() {
  showLoadingSpinner(); // ローディング表示開始

  try {
    const [user, posts, comments] = await Promise.all([
      fetch("/api/user").then(res => res.json()),
      fetch("/api/posts").then(res => res.json()),
      fetch("/api/comments").then(res => res.json())
    ]);

    console.log("✅ すべて成功", { user, posts, comments });
  } catch (err) {
    console.error("❌ どれか失敗しました:", err);
  } finally {
    hideLoadingSpinner(); // 成否に関わらず必ず実行!
    console.log("🔁 ローディング終了");
  }
}

loadAllDataGood();
JavaScript

🧭 実行順:

showLoadingSpinner()
→ Promise.all(並列実行)
→ 成功 or 失敗
→ finally(hideLoadingSpinner)

ポイント

  • Promise.all のどれか1つでも失敗すると 即 catch に飛ぶ
  • でも finally は必ず実行されるので、「後処理(UI片付け)」が安全。

発展:部分成功を許容する(Promise.allSettled 版)

「どれか失敗しても、成功した分は使いたい」
というケースでは Promise.allSettled() が便利です。

async function loadAllDataSafe() {
  showLoadingSpinner();

  try {
    const results = await Promise.allSettled([
      fetch("/api/user").then(r => r.json()),
      fetch("/api/posts").then(r => r.json()),
      fetch("/api/comments").then(r => r.json())
    ]);

    const user = results[0].status === "fulfilled" ? results[0].value : null;
    const posts = results[1].status === "fulfilled" ? results[1].value : [];
    const comments = results[2].status === "fulfilled" ? results[2].value : [];

    console.log("部分結果", { user, posts, comments });
  } finally {
    hideLoadingSpinner();
    console.log("UIを確実に片付けました");
  }
}
JavaScript

これで:

  • どれか失敗してもエラーで止まらない
  • finally で確実にローディング終了
  • status を見て部分的に処理できる

まとめ

状況推奨パターン理由
全部成功が前提Promise.all + finally簡潔・安全
一部失敗を許容Promise.allSettled + finally柔軟性が高い
UI 後処理が必要finally 内に記述成否を問わず実行される

コツの一言

finally は「終わった後の片付け専用」。
並列処理の完了を待つには Promise.all
部分成功を扱うには Promise.allSettled

タイトルとURLをコピーしました