導入: なぜエラーハンドリングが重要なのか
どんなに堅牢なコードを書いても、外部APIの仕様変更、予期せぬユーザー入力、ネットワークの一時的な切断など、実行時にエラーは必ず発生します。JavaScriptにおいてエラーが適切に処理されないと、スクリプトが即座に停止(クラッシュ)し、ユーザー体験を著しく損ないます。try..catch構文を正しく使うことで、エラー発生時にもアプリケーションを安全に継続させたり、ユーザーに親切なフィードバックを返したりすることが可能になります。
基礎知識: try..catchの仕組み
try..catch構造は、エラーが発生しそうなコードを「試行(try)」し、エラーが発生した瞬間に「捕捉(catch)」して、プログラムが停止するのを防ぐ仕組みです。
・tryブロック: 実行したい処理を記述します。ここでエラーが起きると、即座にcatchブロックへ制御が移ります。
・catchブロック: エラーが発生した場合にのみ実行されます。エラーの内容(エラーオブジェクト)を受け取り、ログ出力や復旧処理を行います。
・ランタイムエラーのみ対象: この構文は、プログラムの実行中に発生する「ランタイムエラー」を捕捉します。文法エラー(構文ミス)は実行前に解析されるため、try..catchでは捕捉できません。
実装/解決策: 具体的な手順
実務では、単にエラーを握りつぶすのではなく、エラーの種類に応じて処理を分ける「再スロー(re-throwing)」と、処理の完了を保証する「finally」が重要です。
サンプルプログラム
以下のコードは、JSONのパース処理を例にした実用的なエラーハンドリングのパターンです。
// JSONをパースし、特定のプロパティを検証する関数
function parseUserData(jsonString) {
try {
const user = JSON.parse(jsonString);
// 必須データがない場合は独自エラーを投げる
if (!user.name) {
throw new Error("データ不備: nameがありません");
}
return user;
} catch (err) {
// 発生したエラーの種類によって処理を分岐
if (err instanceof SyntaxError) {
console.error("JSON形式が不正です:", err.message);
} else if (err.message.includes("データ不備")) {
console.warn("バリデーションエラー:", err.message);
} else {
// 想定外のエラーは再スローして上位に伝える
throw err;
}
} finally {
// エラーの有無にかかわらず必ず実行される処理
console.log("パース処理を終了しました。");
}
}
// 実行例
parseUserData('{"name": "田中"}'); // 正常終了
parseUserData('{"age": 25}'); // バリデーションエラー
応用・注意点: 現場で役立つTips
1. 再スロー(Re-throwing)の重要性:
catchブロック内で発生したエラーをすべて握りつぶすと、バグの発見が困難になります。処理できないエラーは必ず `throw err` で再スローし、デバッグしやすい状態を保ちましょう。
2. finallyの正しい使い所:
`finally`は、`try`や`catch`内で`return`や`throw`が実行された場合でも必ず最後に実行されます。ファイル接続のクローズ、ローディングスピナーの非表示、計測終了処理など、「どのような結末であっても必ず実行すべき後処理」に最適です。
3. 非同期処理への注意:
`setTimeout`や`fetch`などの非同期処理内で発生したエラーは、外側の`try..catch`では捕捉できません。非同期関数の内部に改めて`try..catch`を記述するか、`async/await`と組み合わせて適切にハンドリングしてください。
4. グローバルなエラー対策:
万が一の事態に備え、ブラウザ環境であれば `window.onerror` を設定しておくことで、キャッチ漏れのエラーを検知・ログ送信し、事後の分析に役立てることができます。