Javascript

Promise の状態とエラーハンドリング完全ガイド | 実務で使えるベストプラクティス

ⓘ本ページはプロモーションが含まれています

もっとスキルを活かしたいエンジニアへ

スポンサードリンク
働き方から選べる

無料で使えて良質な案件の情報収集ができるサービス

エンジニアの世界では、「いつでも動ける状態を作っておけ」とよく言われます。
技術やポートフォリオがあっても、自分に合う案件情報を日常的に見れていないと、いざ動こうと思った時に比較や判断が難しくなってしまいます。
普段から案件情報が集まる環境を作っておくと、良い案件が出た時にすぐ動きやすくなりますよ。
筆者自身も、メガベンチャー勤務時代に年収1,500万円を超えた経験があります。振り返ると、技術だけでなく「どんな案件や働き方があるか」を日頃から見ていたことが、キャリアの選択肢を広げるきっかけになりました。
このブログを読んでくれた方に感謝を込めて、実際に使っている情報収集サービスを紹介します。

フルリモート・週3日・高単価、どんな条件も妥協したくないなら

フリーランスボードに無料会員登録する

利用者10万人以上。業界最大規模45万件の案件。AIマッチ機能や無料の相場情報が人気。

年収800万円以上のキャリアアップ・ハイクラス正社員を視野に入れているなら

Beyond Careerに無料相談する

内定獲得率90%以上。紹介先企業とは役員クラスのコネクションがある安心と信頼できるエージェント。


スポンサードリンク

Promise の状態とエラー伝搬の基礎

Promise は非同期処理を抽象化したオブジェクトで、pending → fulfilled / rejected と 2 段階に遷移します。実務で堅牢なエラーハンドリングを行うには、各状態が次の thencatch にどのように伝搬するかを正しく理解しておくことが不可欠です。本セクションでは、Promise の基本的なライフサイクルと例外が自動的に reject へ変換される仕組みを解説します。

  • fulfilled: resolve が呼び出されたとき。then の第一引数に渡したコールバックが実行され、戻り値は次の Promise に伝搬します。
  • rejected: reject が呼ばれたか、thencatch 内で例外がスローされたとき。最も近い catch(または then の第二引数)へ制御が移ります。

MDN の解説[^1] と ECMAScript 仕様[^2] によれば、then コールバック内で throw が発生すると、その瞬間に生成された Promise は自動的に rejected 状態となり、チェーン上の次の catch が呼び出されます。したがって「throwcatch されない」ケースは、例外が Promise の外側(例えば同期コードで直接 throw)で発生したときに限られます。


then と catch の正しい組み合わせ

このセクションでは、thencatch を組み合わせたときのエラー伝搬パターンを具体例で示し、意図しない抜け漏れを防ぐコツを紹介します。

1. 基本的なエラーパイプライン

then の中でスローされた例外は同じチェーン内の次の catch が捕捉します。返却した Promise をそのまま続けることが重要です。

2. then の第二引数は非推奨

then(onFulfilled, onRejected) の形でエラーハンドリングを書くこともできますが、可読性と一貫性の観点から catch を別行に書く方が好ましいと多くのガイドラインで推奨されています[^3]。


チェーン途中での catch 活用法

実務では処理を段階的に分割し、各ステージごとに専用のエラーハンドラを配置することが多いです。このセクションでは「最も近い catch が優先される」という特性を利用した設計パターンを解説します。

1. ステージ別ハンドリング

  • 利点: 前段で捕捉したエラーは下流へ伝搬しないため、不要な処理を回避できる。
  • 注意点: catch 内で再スローしない限り、後続のチェーンは 成功扱いthen が実行)になる。

2. フォールバック値によるリカバリ

エラー発生時に代替データを返すことで、以降のロジックを中断せずに進められます。特に外部 API が不安定な場合に有効です。


async/await と try‑catch の実務パターン

async 関数は内部で await した Promise が reject されたときに例外をスローします。これを従来の try‑catch で捕捉すれば、同期コードと同様の可読性が得られます。

1. 基本形

  • 可読性: awaittry‑catch の組み合わせはエラーロジックが分散せず、一箇所に集約できる。
  • 粒度の調整: 必要なら内部でさらに try‑catch を入れ、外部 API と DB それぞれの失敗を個別にハンドリング可能。

2. エラーログの標準化

実務ではログの形式を統一するとモニタリングが楽になります。以下は JSON 形式で出力する例です。


Node.js 環境での未捕捉エラー対策

Node.js では 未処理の Promise リジェクト がプロセス終了につながることがあります。サービス停止を防ぐため、グローバルハンドラの設定は必須です。

1. unhandledRejection のハンドリング

  • 開発時はスタックトレースをコンソールに出力し、原因特定に役立てます。
  • 本番時は JSON ログを外部モニタリング(例:Datadog, Sentry)へ送信し、プロセスを速やかに再起動させることでダウンタイムを最小化します。

2. 同時に uncaughtException も設定

同期的な例外は process.on('uncaughtException') で捕捉できますが、可能な限り個別の try‑catch で事前に処理する設計が推奨されます。グローバルハンドラは最終手段として位置付けるべきです。


複数 Promise の同時実行とエラーハンドリング

複数の非同期タスクを並列に走らせる場合、失敗時の挙動を意識した API 選択が重要です。ここでは Promise.allPromise.allSettled の使い分けを中心に解説します。

1. Promise.all の特性

特徴 エラー発生時の挙動
即時リジェクト どれか 1 つが reject になると全体が reject となり、残りの結果は破棄される。

2. Promise.allSettled の特性

特徴 エラー発生時の挙動
全結果取得 各 Promise の成功・失敗を個別に保持し、すべてのステータスが返る。

  • エラーオブジェクトの統一: reason は通常 Error インスタンスですが、ログ解析しやすいように {name, message, code, details} の形で整形すると便利です(Node.js 公式ガイドライン[^4])。

よくある落とし穴と安全な回避策

Promise チェーンは見た目がシンプルでも、細かいミスがエラー捕捉の失敗につながります。特に then 内で 同期的に throw した場合の挙動を正しく理解しておくことが重要です。

1. then 内での throw が期待通りに働かないケース

上記のコードは実際には catch が確実に呼び出されますthen のコールバック内でスローされた例外は内部で生成された Promise に対して reject とみなされ、直後の catch が捕捉します。「捕捉できない」ケースは次の二つです。

  1. throw が Promise チェーンの外側で実行された(例:同期コードブロック内で直接 throw した場合)。
  2. then の戻り値を返さずに別スレッドで例外が発生したsetTimeout(() => { throw ... }) 等)。

安全な実装パターン

  1. async/await に置き換える
    js
    async function getItem() {
    try {
    const res = await fetch('/api/item');
    if (!res.ok) throw new Error('Bad response');
    return await res.json();
    } catch (err) {
    console.error('Handled in async:', err);
    throw err; // 必要なら上位へ再スロー
    }
    }

  2. Promise.resolve でラップする
    js
    fetch('/api/item')
    .then(res => Promise.resolve().then(() => {
    if (!res.ok) throw new Error('Bad response');
    return res.json();
    }))
    .catch(err => console.error('Caught after wrap:', err));

  3. ヘルパー関数で統一的にエラーハンドリング
    js
    function safeThen(promise, fn) {
    return promise.then(v => Promise.resolve().then(() => fn(v)));
    }

safeThen(fetch('/api/item'), res => {
if (!res.ok) throw new Error('Bad response');
return res.json();
}).catch(err => console.error('Handled via helper:', err));

2. catch の中で再スローし忘れる

catch 内でエラーをログだけして黙って終了すると、下流の then が成功扱いで実行されてしまうことがあります。意図的に処理を止めたい場合は必ず throw するか、代替値を返すかを明示してください。


記事全体の要点まとめ

  • Promise の状態 と例外が自動的に reject に変換される仕組みを正しく理解すれば、基本的なエラーハンドリングはシンプルになる。
  • catch はチェーン途中でも有効であり、最も近いハンドラへエラーが集約される特性を活かすとコードの可読性が向上する。
  • async/await + try‑catch はエラーロジックを一箇所にまとめられ、実務で推奨される設計パターンである。
  • Node.js の未捕捉リジェクトは必ずハンドリングし、環境別(開発/本番)に適した対策を講じることが安全運用の鍵となる。
  • 並行処理では Promise.allPromise.allSettled を使い分け、失敗時の挙動を意図的にコントロールする。
  • then 内での throw は内部で reject になるが、外部でスローした例外は別途捕捉が必要。安全なラップや async/await への置き換えで落とし穴を回避できる。

これらのベストプラクティスを組み合わせれば、実務レベルで堅牢かつ保守性の高い JavaScript のエラーハンドリングが実現できます。


[^1]: MDN Web Docs, “Promise”, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[^2]: ECMA‑262 第12版, 25.4 Promise Objects, https://tc39.es/ecma262/#sec-promise-objects
[^3]: 「Effective JavaScript」(David Herman) – Promise の使用上のベストプラクティス、章 6.5
[^4]: Node.js Documentation, “Error handling”, https://nodejs.org/api/process.html#process_event_unhandledrejection

スポンサードリンク

もっとスキルを活かしたいエンジニアへ

スポンサードリンク
働き方から選べる

無料で使えて良質な案件の情報収集ができるサービス

エンジニアの世界では、「いつでも動ける状態を作っておけ」とよく言われます。
技術やポートフォリオがあっても、自分に合う案件情報を日常的に見れていないと、いざ動こうと思った時に比較や判断が難しくなってしまいます。
普段から案件情報が集まる環境を作っておくと、良い案件が出た時にすぐ動きやすくなりますよ。
筆者自身も、メガベンチャー勤務時代に年収1,500万円を超えた経験があります。振り返ると、技術だけでなく「どんな案件や働き方があるか」を日頃から見ていたことが、キャリアの選択肢を広げるきっかけになりました。
このブログを読んでくれた方に感謝を込めて、実際に使っている情報収集サービスを紹介します。

フルリモート・週3日・高単価、どんな条件も妥協したくないなら

フリーランスボードに無料会員登録する

利用者10万人以上。業界最大規模45万件の案件。AIマッチ機能や無料の相場情報が人気。

年収800万円以上のキャリアアップ・ハイクラス正社員を視野に入れているなら

Beyond Careerに無料相談する

内定獲得率90%以上。紹介先企業とは役員クラスのコネクションがある安心と信頼できるエージェント。


-Javascript