JavaScript | 例外処理(try…catch) のポイント

JavaScript
スポンサーリンク

JavaScriptのエラー処理の基本

エラーは「プログラムが思ってない動きをしたときに出るサイン」。怖がるより、どう受け止めて次に進むかが大事です。ここでは、初心者にもわかるように、try...catchでエラーを受け止める方法を、例題つきで丁寧に説明します。


エラーを受け止める仕組み(try…catch)

  • 役割:
    tryの中でコードを実行し、エラーが起きたらcatchがそれを受け取って処理します。
  • 最小例:
try {
  // ここでエラーになる(存在しない関数を呼び出し)
  notDefinedFunction();
  console.log("ここは実行されません");
} catch (error) {
  console.log("エラーをキャッチしました:", error.message);
}
console.log("プログラムは止まらずに続行します");
JavaScript
  • ポイント:
    • エラーが出てもプログラム全体を止めないで、次の処理に進める。
    • error.messageには読みやすい説明が入ります。必要ならerror.nameerror.stackも使えます。

関数の中のエラーは呼び出し元でキャッチできる(同期処理)

  • イメージ:
    関数の中で起きた事故も、外側のtry...catchで受け止められる。
  • 例題1:型の組み合わせミス(NumberとBigInt)
function add(a, b) {
  // ここでエラーが発生(NumberとBigIntは直接足せない)
  return a + b;
}

try {
  console.log(add(10, 8));   // 18(OK)
  console.log(add(10, 8n));  // ここでエラー
} catch (error) {
  console.log("キャッチ:", error.message); // e.g. "Cannot mix BigInt and other types"
}
console.log("続行可能");
JavaScript
  • 例題2:入れ子の関数でもOK
function calcTotal(a, b) {
  return addStrict(a, b);
}
function addStrict(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new Error("数値だけを足してください");
  }
  return a + b;
}

try {
  console.log(calcTotal(5, 7));     // 12(OK)
  console.log(calcTotal(5, "7"));   // ここでErrorをthrow → catchへ
} catch (error) {
  console.log("キャッチ:", error.message); // "数値だけを足してください"
}
JavaScript
  • ポイント:
    • 同期的(その場で直ちに実行)される関数の中のエラーは、呼び出し元のcatchで受け止められる。
    • エラーを自分で出したいときはthrow new Error("メッセージ")を使う。

非同期処理の落とし穴(外側のtry…catchでは拾えない)

  • イメージ:
    setTimeoutやイベントのコールバックは、あとで別タイミングで実行されます。外側のtry...catchは、その「あとで」のエラーを見張れません。
  • 悪い例(キャッチされない):
try {
  setTimeout(() => {
    // ここでエラーが起きても、外側のcatchには届かない
    console.log(10 + 8n);
  }, 1000);
} catch (error) {
  console.log("これは呼ばれない");
}
JavaScript
  • 正しい例(コールバック内でキャッチする):
setTimeout(() => {
  try {
    console.log(10 + 8n); // エラー発生
  } catch (error) {
    console.log("コールバック内でキャッチ:", error.message);
  }
}, 1000);
JavaScript
  • ポイント:
    • 非同期のエラーは、その非同期の中(コールバックやPromiseチェーン)でキャッチする。
    • 外側のtry...catchで包んでも意味がないことが多い。

非同期の正しいエラー処理(Promise/async)

  • Promiseの.catchを使う:
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 失敗した想定
      reject(new Error("通信に失敗しました"));
    }, 500);
  });
}

fetchData()
  .then((data) => {
    console.log("成功:", data);
  })
  .catch((error) => {
    console.log("キャッチ:", error.message);
  });
JavaScript
  • async/awaitとtry…catchを組み合わせる:
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("通信に失敗")), 500);
  });
}

async function main() {
  try {
    const data = await fetchData(); // rejectされるとcatchへ
    console.log("成功:", data);
  } catch (error) {
    console.log("キャッチ:", error.message);
  } finally {
    console.log("片付け処理(必ず実行)");
  }
}

main();
JavaScript
  • ポイント:
    • Promise: 失敗はreject、受け止めは.catch
    • async/await: 非同期でもtry...catchが使える。awaitのところで失敗したらcatchに流れる。
    • finally: 成否に関係なく片付けしたい処理を入れる。

よくあるミスと実践のコツ

  • ミス1:非同期のエラーを外側でキャッチしようとする
    • 対策: コールバックの中、またはPromise/asyncの中でキャッチする。
  • ミス2:雑なcatchで原因がわからない
    • 対策:
      • ログを丁寧に: console.error(error.name, error.message);
      • 必要なら再throw: ここでは握りつぶさない、上位に任せたいときはthrow
  • ミス3:型を混ぜる(NumberとBigIntなど)
    • 対策: 入力チェックをして、想定外をthrowする。
function safeAdd(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("number同士のみ加算できます");
  }
  return a + b;
}
JavaScript
  • 小さく試す:
    問題が起きそうな箇所だけをtry...catchで囲んで、ログを出す。広く囲いすぎると原因追跡が難しくなる。

練習問題(手を動かして理解する)

  • 問題1:同期関数のエラー
    • 課題: divide(a, b)を作り、b0ならError("ゼロでは割れません")を投げる。外側でキャッチしてメッセージを表示。
    • ヒント:
function divide(a, b) {
  if (b === 0) throw new Error("ゼロでは割れません");
  return a / b;
}
try {
  console.log(divide(10, 0));
} catch (e) {
  console.log("キャッチ:", e.message);
}
JavaScript
  • 問題2:非同期のエラー(Promise)
    • 課題: getUser(id)が存在しないIDならreject.catchでメッセージを表示。
function getUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      id === 1 ? resolve({ id: 1, name: "Alice" })
               : reject(new Error("ユーザーが見つかりません"));
    }, 300);
  });
}

getUser(2)
  .then(user => console.log("成功:", user))
  .catch(err => console.log("キャッチ:", err.message));
JavaScript
  • 問題3:async/await+finally
    • 課題: loadConfig()asyncで呼び出し、失敗時にcatch、最後にfinallyで「終了」を表示。
function loadConfig() {
  return Promise.reject(new Error("設定ファイルが壊れています"));
}

async function boot() {
  try {
    const cfg = await loadConfig();
    console.log("成功:", cfg);
  } catch (e) {
    console.log("キャッチ:", e.message);
  } finally {
    console.log("終了");
  }
}
boot();
JavaScript
タイトルとURLをコピーしました