Javascript

JavaScript 非同期処理と実務的エラーハンドリング完全ガイド

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

Contents

スポンサードリンク

1️⃣ 非同期処理とエラーハンドリングの基礎

概要

  • Promisepending → fulfilled / rejected の一方向遷移だけを持ち、失敗時は必ず reject が伝搬します。
  • .then で成功ハンドラ、.catch で失敗ハンドラを定義すれば、例外がスローされても どこかで必ず捕捉 できる構造になります。

Promise の状態遷移と .then/.catch の関係

状態 説明
pending 非同期処理がまだ完了していない。
fulfilled 正常に結果が取得できた → .then が呼び出される。
rejected エラーが発生した → .catch(または次の .then(null, …))が呼び出される。

.catch はチェーン全体の 唯一の出口 になるため、意図しない例外がコンソールに流出するリスクを低減できます。

実装例 – API 呼び出しの Promise ラッパー

補足

  • HttpError は後述の カスタム Error の一例です。
  • .catchチェーン全体 の失敗を捕捉し、必要に応じて再スローしている点が重要です。

ベストプラクティス

  1. エラーは必ず reject に変換 する(HTTP エラーや外部ライブラリの例外も同様)。
  2. .catch 内で ロギングと再スロー を行い、上位層に責務を委譲。
  3. 可能なら エラーメッセージはユーザー向けとデバッグ用に分離(例: err.userMessageerr.stack)。

要点

  • Promise の .then/.catch パターンは、失敗が必ず一箇所で捕捉できる安全な基盤です。
  • HTTP ステータスや外部サービスのエラーメッセージは reject に変換し、上位へ伝搬させましょう。

2️⃣ async/await と try‑catch の実務的活用例

概要

async / await可読性 が高く、直列処理を自然なコードフローで記述できます。
エラーハンドリングは従来の try‑catch をそのまま利用できるため、ロジックごとに局所的に捕捉 する設計が推奨されます。

try‑catch の位置づけ

  • await がスローした例外は 同期コードと同様に throw され、最も近い try ブロックで捕捉できます。
  • エラーハンドリングを ビジネスロジック層インフラ層(Express 等) に分離するとテストが容易です。

実装例 – 注文処理フロー

ポイント解説

  • try‑catchロジック単位 に閉じているため、スタックトレースから失敗箇所が直感的に分かります。
  • エラー種別(ServiceError)で HTTP ステータスコードを決定するパターンは実務で頻出です。

ベストプラクティス

項目 推奨内容
エラーログ catch 内だけで行い、再スロー して上位ハンドラに伝搬させる。
エラークラス ビジネスロジックごとに固有のカスタムエラーを作成し、コード で判定できるようにする。
テスト戦略 processOrder のような関数は ユニットテストで try 部分だけをスタブ化 し、例外フローも検証できるように設計。

要点

  • async/await + try‑catch は直列非同期処理でも自然にエラーハンドリングが行える構造です。
  • エラーは ロギングだけで止め、必要なら上位へ再スロー して責務を分離しましょう。

3️⃣ エラー構造化とカスタム Error クラス

概要

プロジェクト全体で統一されたエラーフォーマットを持つことは、ログ解析・モニタリング・クライアントレスポンス の品質向上に直結します。
Error を継承したカスタムクラスにメタ情報(status, code, context)を付与すれば、型安全かつ情報量の多い例外オブジェクトが実現できます。

カスタムエラー設計指針

  1. 必須プロパティmessage, status(HTTP ステータス)、code(アプリ独自コード)
  2. 任意プロパティcontext(リクエスト ID などの付随情報)
  3. 継承時の注意点Object.setPrototypeOf(this, new.target.prototype) または Error.captureStackTrace を使用し、instanceof 判定が正しく機能するようにする。

JavaScript 実装例

TypeScript 実装例

Express での一元ハンドラ例

ベストプラクティス

  • エラー生成はユーティリティ関数toAppError(err))で統一し、外部ライブラリの例外もすべて AppError に変換。
  • ロギングはメタ情報を含めて出力 することで、後から検索・集計しやすくなる(例: Sentry のタグ付与)。
  • テスト時はカスタムエラーの型とプロパティを検証 すると、ミスが早期に判明。

要点

  • status, code, context を持つカスタム Error により、ロギング・モニタリング・クライアント応答 が一元管理できます。
  • TypeScript と併用すれば型安全が担保され、チーム全体で統一したエラーレスポンス戦略を実装可能です。

4️⃣ 責務分離・再スロー・ロギングのベストプラクティス

概要

エラーハンドリングは 「捕捉」→「ロギング」→「再送(再スロー)」 の三層に分割すると、テストしやすく副作用が最小化されます。
単一の catch に全ての処理を書き込むと、二重ロギングや予期せぬ例外伝搬が起こりやすくなります。

層化設計パターン

  1. 捕捉層 – エラー種別判定・最低限の変換だけを行う。
  2. ロギング層 – 上位で一括してログ出力し、エラーレベル(info/warn/error)を分岐させる。
  3. 再送層 – 必要に応じて例外を再スローし、上位ハンドラが共通処理(トランザクションロールバック等)を実装できるようにする。

コード例

プロセスレベルの保護策

ベストプラクティスまとめ

  • 捕捉層は変換だけ に留め、ビジネスロジックに影響させない。
  • ロギングは一元化(例: winston, pino)し、エラーレベルを適切に設定。
  • 再送は必ず throw して上位ハンドラに委譲、テスト時は jest.spyOn 等で捕捉層だけスタブ化可能。

要点

  • エラー捕捉・ロギング・再スローを明確に分離すれば、テストが容易で副作用が抑制された堅牢なコードになります。
  • プロセスレベルのハンドラで未捕捉例外も網羅的に処理することが運用上必須です。

5️⃣ 並列処理と Node.js 環境での未捕捉エラー対策

概要

Promise.all 系の並列実行では 失敗戦略 を設計段階で決める必要があります。
さらに、Node.js ではプロセスレベルの例外監視を忘れずに設定し、部分的な障害がシステム全体ダウンにつながらないようにします。

Promise.allPromise.allSettled の比較

項目 Promise.all Promise.allSettled
失敗時の挙動 いずれかが reject → 即座に全体が rejected 全タスクの結果(fulfilled / rejected)をすべて取得
成功データ取得 成功した場合のみ配列で返却 statusvalue/reason が格納されたオブジェクト配列
用途例 全体成功が前提 のバッチ処理 部分成功でも続行したい 場合やリトライ戦略に利用

実装例 – 全体失敗として扱う(シンプル)

実装例 – 部分成功・部分失敗を個別処理(allSettled)

部分成功の活用例

  • リトライキューfailures をキューに入れ、バックオフ付きで再度取得。
  • UI 表示:成功データは即表示し、失敗した項目だけスケルトンやエラーメッセージを出す。

選択指針

  1. 全体が必ず揃う必要がある 場合は Promise.all
  2. 部分的にでも表示/処理できる なら allSettled + カスタムハンドラ。

プロセスレベル対策(再掲)

要点

  • Promise.allPromise.allSettled の特性を踏まえて 失敗ハンドリング方針 を設計する。
  • Node.js では必ず プロセスレベルの例外監視 を実装し、部分障害でもシステム全体が停止しない堅牢性を確保します。

6️⃣ 実務コード例とデバッグテクニック

概要

本稿で紹介したベストプラクティスを サンプルプロジェクト に落とし込み、VSCode デバッガや Chrome DevTools を活用すれば、非同期エラーの原因特定が格段に楽になります。

サンプルプロジェクト構成(簡易版)

VSCode デバッガ設定例(.vscode/launch.json)

  • --inspect‑brk により起動直後にブレークし、最初の await 行までステップ実行できます。

Chrome DevTools で非同期スタックトレースを確認する手順

  1. ブラウザで対象ページを開き F12 → Sources タブへ。
  2. 左側ツリーからデバッグしたスクリプトを選び、await が書かれた行に ブレークポイント を設定。
  3. 実行すると右下の Async call stack に Promise のチェーンが表示され、例外発生箇所が可視化されます(Chrome 115 以降推奨)。

デバッグ時チェックリスト

  • [ ] catch 内でエラーを 再スロー しているか
  • [ ] ログ出力は 一元化(例: winston)されているか
  • [ ] カスタム Error に必須プロパティ (status, code) が設定済みか
  • [ ] Promise.allSettled を使用した場合、result.status 判定漏れがないか
  • [ ] プロセスレベルハンドラが起動時に確実に登録されているか

ベストプラクティスまとめ

項目 推奨アクション
デバッグ await 行でブレークし、Async call stack を確認。
ロギング ログレベルはエラー種別で分岐(例: error → 5xx, warn → 4xx)。
テスト ユニットテストでは捕捉層だけスタブ化し、再スローやロギングが正しく伝搬するか検証。
CI/CD ビルド時に node --trace-warnings を有効にし、未処理例外を早期発見。

要点

  • VSCode デバッガと Chrome DevTools の非同期スタックトレース活用で、Promise/async 系のバグを迅速に特定できます。
  • サンプルプロジェクトでベストプラクティスを体感し、実務コードへ即座に適用しましょう。

🎯 まとめ(全体要点)

  1. Promise.then/.catch により失敗が必ず捕捉できる安全な基盤。
  2. async/await + try‑catch は直列処理でも自然にエラーハンドリングが可能で、ロジック単位の責務分離がしやすい。
  3. カスタム Error クラスstatus, code, context を統一管理すれば、ログ・モニタリング・クライアント応答が一元化できる。
  4. エラーハンドリングは 捕捉 → ロギング → 再スロー の三層に分離し、テスト容易性と副作用抑制を実現する。
  5. 並列処理は Promise.allPromise.allSettled の特性 を踏まえて失敗戦略を選択し、Node.js のプロセスレベルハンドラで未捕捉例外も網羅的に処理する。
  6. デバッグは VSCode デバッガ+Chrome DevTools で非同期スタックトレースを確認し、チェックリストで抜け漏れを防止。

これらの指針とコードスニペットをプロジェクトに組み込めば、堅牢かつ保守性の高い非同期処理基盤 を構築できます。 🚀

スポンサードリンク

-Javascript
-, , , , , , ,