【JavaScript】try-catch文を使ったエラーハンドリングの基本

JavaScriptでエラーが起きてもプログラムを止めずに処理を続けるには、try-catch 文を使います。エラーを捕捉(catch)して対処することで、アプリケーションの安定性を高められます。

この記事では、基本構文から、throw で自分でエラーを出す方法、async/awaitでのエラー処理、そしてtry-catchで捕まえられないエラーまで解説します。

この記事の結論:try { 処理 } catch (error) { 対処 } finally { 後処理 } が基本形です。エラー内容は error.message / error.name で取得します。async/awaitも同じ try-catch で扱え、自分でエラーを出すには throw new Error("...") を使います。
スポンサーリンク

基本構文(try / catch / finally)

エラーが起きる可能性のあるコードを try に書き、エラー時の処理を catch に書きます。finallyエラーの有無に関わらず必ず実行される後処理です(省略可)。

JavaScript:try / catch / finally
try {
  const data = JSON.parse(text); // エラーが起きる可能性のある処理
  console.log(data);
} catch (error) {
  console.error("エラー:", error.message); // エラー時の処理
} finally {
  console.log("終了処理"); // 成否に関わらず必ず実行
}

エラーオブジェクトが不要なら、引数を省いて catch { ... } とも書けます(ES2019以降)。リソース解放やローディング非表示など、確実に行いたい後処理は finally に置きます。

自分でエラーを投げる(throw)

想定外の値が来たときなど、自分でエラーを発生させたいときは throw を使います。throw new Error("メッセージ") が基本です。

JavaScript:throwでエラーを投げる
function divide(a, b) {
  if (b === 0) {
    throw new Error("0で割ることはできません");
  }
  return a / b;
}

try {
  divide(10, 0);
} catch (error) {
  console.error(error.message); // "0で割ることはできません"
}

エラーの種類を見分ける(instanceof)

エラーには種類があり、error.name で名前を、instanceof で型を判別できます。組み込みの型には TypeErrorSyntaxErrorRangeErrorReferenceError などがあります。

JavaScript:エラー型で分岐
try {
  null.foo; // TypeError が発生
} catch (error) {
  console.log(error.name);    // "TypeError"
  console.log(error.message);

  if (error instanceof TypeError) {
    console.log("型に関するエラー");
  } else if (error instanceof SyntaxError) {
    console.log("構文エラー");
  }
}

たとえば JSON.parse() は不正な文字列で SyntaxError を投げます。JSONの読み込みとエラー処理はJSONファイルの読み込み方も参考になります。

async/awaitでのエラー処理

現代のJavaScriptで最もよく使うのが、async関数内の try-catchです。await したPromiseがrejectされると、その例外を catch で捕捉できます。Promiseチェーンの .catch() より読みやすいのが利点です。

JavaScript:async/awaitのtry-catch
async function loadData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`); // 失敗を自分でthrow
    }
    return await response.json();
  } catch (error) {
    console.error("読み込み失敗:", error.message);
  } finally {
    console.log("通信完了");
  }
}

async/await の基本はPromiseとasync/awaitの使い方fetch の通信はfetch APIで非同期通信を行うで詳しく解説しています。

try-catchで捕まえられないエラーに注意

try-catch同期的に実行される範囲のエラーしか捕捉できません。次のような別タイミングで実行されるエラーは捕まえられないので注意してください。

JavaScript:捕捉できない例(NG)
// NG: setTimeout のコールバック内のエラーは外のcatchで捕まえられない
try {
  setTimeout(() => {
    throw new Error("捕捉されない");
  }, 1000);
} catch (error) {
  // ここには来ない(別タスクで実行されるため)
}

// NG: await しない Promise のエラーも捕捉できない
try {
  fetch("/data"); // await が無い
} catch (error) {
  // rejectされてもここには来ない
}
対処:setTimeout などコールバック内のエラーは、そのコールバックの中で try-catch します。Promiseは await するか .catch() を付けることで捕捉できます。「同期的に実行される所だけ捕まえられる」と覚えておきましょう。

よくある質問(FAQ)

QJavaScriptのtry-catchはいつ使うべきですか?
A例外が発生する可能性のある処理(JSON.parsefetch・外部API呼び出しなど)の周囲に使います。一方、値が undefined かどうかなど事前に確認できるものは、try-catchより条件チェック(if)の方が適切な場合もあります。
Qfinallyブロックはどんなときに使いますか?
Aエラーの有無にかかわらず必ず実行したい処理(リソースの解放、ローディング表示の非表示、接続のクローズなど)に使います。trycatch どちらが実行されても確実に実行されます。
Q非同期関数(async/await)でのエラーハンドリングは?
Aasync関数内で try-catch-finally をそのまま使えます。await でrejectされたPromiseは catch で捕捉できます。Promiseチェーンの .catch() より可読性が高く推奨されます。
QsetTimeout内で投げたエラーが捕まえられないのはなぜ?
AsetTimeout のコールバックは後で別タスクとして実行されるため、外側の try-catch(すでに実行が終わっている)では捕捉できません。コールバックの中で try-catch してください。

まとめ

try-catch は、try で処理、catch でエラー対処、finally で必ず行う後処理、というのが基本です。エラー内容は error.message / error.name、種類は instanceof で判別できます。

自分でエラーを出すなら throw new Error()async/awaitのエラーも同じ try-catch で扱えます。ただし setTimeout 内や await しないPromiseのエラーは外のtry-catchでは捕まえられない点に注意しましょう。