フロントエンド開発において、予期せぬエラーは避けて通れません。しかし、多くの現場で「ログを吐いて終わり」という表面的な対応に留まっているケースを見かけます。今回は、JavaScriptの実行エンジンがエラーをどう処理しているのか、コールスタックの構造から紐解き、実務で使える一歩進んだハンドリング手法を解説します。
コールスタックから読み解く「エラーの発生源」
JavaScriptはシングルスレッドで動作し、関数呼び出しの履歴を「コールスタック」というスタック構造で管理しています。エラーが発生した際、ブラウザのコンソールにはスタックトレースが表示されますが、これを単なる「場所の特定」として使うのはもったいないです。
実務において重要なのは、スタックトレースを階層構造として捉えることです。例えば、非同期処理のチェインでエラーが起きた場合、スタックトレースが途切れてしまい、本来の呼び出し元が見えなくなることがあります。これを防ぐために、あえて新しいErrorオブジェクトを生成し、元のエラーをcauseプロパティに格納する「Error Cause」の活用を推奨します。これにより、スタックを連結させ、エラーの発生コンテキストを維持したまま上位層へ伝播させることが可能になります。
「グローバル」と「ローカル」の境界線を引く
エラーハンドリングで最も陥りやすい罠は、すべてのエラーを一つのtry-catchで囲んでしまうことです。これはスタックの文脈を破壊し、デバッグを困難にします。
私が推奨する戦略は、「エラーの粒度に応じた境界線」を設けることです。
1. UIコンポーネント単位:ErrorBoundaryを配置し、部分的なレンダリング失敗を隔離する。
2. データフェッチ単位:APIクライアント層で、ステータスコードに応じたカスタムエラークラスをスローする。
3. アプリ全体:window.onerrorやunhandledrejectionで、未知の致命的なクラッシュをキャッチし、Sentryなどの監視ツールへコンテキストを送信する。
実務での教訓:エラーを握りつぶさない
現場でよく見かける「空のcatchブロック」は、コールスタックを強制終了させる行為です。スタックが途切れると、なぜその状態になったのかという「経緯」が消失します。
もし、どうしてもエラーを無視したい場合でも、必ずログには残してください。その際、単なるメッセージではなく、関連する状態(ステート)や引数の値をセットで記録することが重要です。コールスタックという「時間軸」の情報に加え、その瞬間の「データ」を補完することで、再現性の低いバグを撲滅できる確率が劇的に向上します。
技術は常に進化していますが、コールスタックという実行エンジンの根本的な仕組みは変わりません。表面的なライブラリの知識だけでなく、こうした実行時の挙動を正しく理解し、堅牢なフロントエンドを構築していきましょう。