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

JavaScript
スポンサーリンク

では、try…catch関数呼び出しの先で例外が起きた場合非同期(コールバック)での例外 を初心者向けに噛み砕いて、たくさんの例題と出力例つきで詳しく説明します。


基本イメージ

  • try { … } catch (e) { … } は「試して(try)、もし例外が出たら捕まえて(catch)処理する」仕組みです。try の中で「例外が発生する可能性のある処理」を囲います。

1) 同期関数を呼び出して、その内部で例外が起きたとき

ポイント: 呼び出し元の try の中で関数を呼んでいるなら、その関数内でエラーが起きても呼び出し元の catch で捕まえられます。

例①(単純)

function sum(a, b) {
  return a + b;
}

console.log('Start');

try {
  console.log(sum(10, 8));   // 正常: 18
  console.log(sum(10, 8n));  // ここで TypeError (BigInt と Number の混在)
} catch (e) {
  console.error('caught:', e.message);
}

console.log('End');
JavaScript

出力(想定)

Start
18
caught: Cannot mix BigInt and other types, use explicit conversions
End

説明:sum(10, 8n) のように BigIntNumber を混ぜると TypeError が出ますが、呼び出しを try の中に書いているので catch で処理され、プログラムは止まりません。


2) 関数が入れ子(多段)になっている場合

ポイント: 例外は呼び出し元に伝搬(スタックをさかのぼる)して、どこかに try…catch があればそこで捕まえられます。

例②(多段)

function inner(a, b) {
  return a + b; // ここで例外が起きる可能性(例:BigInt 混在)
}
function middle(a, b) {
  return inner(a, b);
}
function outer(a, b) {
  return middle(a, b);
}

console.log('Start');
try {
  console.log(outer(10, 8n)); // innerで例外 → outerをさかのぼってcatchで捕まる
} catch (e) {
  console.error('caught at outer:', e.message);
}
console.log('End');
JavaScript

出力(想定)

Start
caught at outer: Cannot mix BigInt and other types, use explicit conversions
End

3) 注意! 非同期のコールバック関数では try…catch が効かない場合がある

重要ポイント(ここで多くの初心者が混乱します)
setTimeout やイベントハンドラなど、非同期に後から呼ばれる関数の中で起きた例外は、外側の try では捕まえられないことがあります。なぜなら、try が実行されているときと、コールバックが実行されるタイミングが別(時間差がある)だからです。

例③(NGパターン)

console.log('Start');

try {
  setTimeout(function sum(a, b) {
    // 1秒後に実行される
    console.log(a + b); // ここで BigInt 混在なら例外
  }, 1000, 10, 8n);
} catch (e) {
  console.error('caught:', e);
}

console.log('End');
JavaScript

実行の流れ(想定)

Start
End
// 1秒後にコールバックが実行され、ここで例外が発生すると
// ブラウザ/ランタイムの「Uncaught TypeError: ...」が出る(外側の catch では捕まらない)

説明:setTimeout 呼び出し自体は同期にすぐ終了する(コールバック呼出しは後)ため、コールバック内の例外は外側の try のスコープ外で発生します。よって外側の catch で捕まえられません。


4) 非同期で例外を安全に扱う方法(実践的な対策)

ここからは実用的な回避策をいくつか示します。どれを使うかは状況に依ります。

A. コールバックの中で個別に try…catch を書く(最も単純)

setTimeout(function (a, b) {
  try {
    console.log(a + b); // ここで例外が起きてもローカルで処理可能
  } catch (e) {
    console.error('caught inside callback:', e.message);
  }
}, 1000, 10, 8n);
JavaScript

B. Promise を使い .catch() で受ける(モダン)

非同期処理が Promise を返すなら、.catch() でエラーを受け取れます。現代的には fetch, async 関数などと相性が良いです。MDN のドキュメントも参考にしてください。

// サンプルの疑似非同期処理を Promise 化
function asyncAdd(a, b) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        resolve(a + b);
      } catch (e) {
        reject(e);
      }
    }, 500);
  });
}

asyncAdd(10, 8n)
  .then(result => console.log(result))
  .catch(err => console.error('promise caught:', err.message));
JavaScript

C. async/await と try…catch を組み合わせる(同期風に書けてわかりやすい)

async 関数内で await しているときは、通常の try…catch で例外(Promise の rejected)を捕まえられます。MDN 参照。

async function doAdd() {
  try {
    const result = await asyncAdd(10, 8n);
    console.log(result);
  } catch (e) {
    console.error('async/await caught:', e.message);
  }
}
doAdd();
JavaScript

D. (補助的)Promise.try / Promise.resolve を使う

最近は Promise.try() のようなユーティリティが使える場面もあり、同期関数の「即時スロー」を Promise の拒否に変換するのに便利です(環境依存)。参考記事あり。


5) 実践的なチェックリスト(初心者がコードを書くときの習慣)

  1. その処理は同期? 非同期? をまず判断する。
    • 同期 → 呼び出し元の try で捕まえられる。
    • 非同期 → コールバックのなかで try を使うか、Promise/.catch、async/await を使う。
  2. 非同期で外側の try に頼らない(頼ってもうまく捕まらないことがある)。
  3. 外部 API 呼び出しやファイル処理など「失敗の確率がある処理」は必ずエラー処理を入れる(ユーザーにエラーメッセージを出す、再試行する、ログを残す等)。

6) 練習問題(自分で試してみよう)

問1. 下のコードを実行して、どこの catch でエラーが捕まるか予想し、実行して確かめてください。

function a() { b(); }
function b() { c(); }
function c() { throw new Error('boom'); }

try {
  a();
} catch (e) {
  console.log('caught at top:', e.message);
}
JavaScript

(期待)caught at top: boom

問2. 次のコードは外側の catch でエラーを捕まえられるか? 実行して確かめてください。

try {
  setTimeout(() => { throw new Error('later boom'); }, 100);
} catch (e) {
  console.log('caught outer:', e.message);
}
JavaScript

(期待)外側の catch では捕まらず、コンソールに Uncaught Error: later boom のように表示される。対処法としてはコールバック内で try を使うか Promise 化して .catch() すること。

問3. async/awaitfetch 等の例を作り、try…catch でエラーを処理してみる(実際の API を使うか、Promise.reject(new Error('fail')) を作って試す)。


まとめ(初心者向け)

  • 同期関数のエラー → 呼び出し元の try で捕まる(伝搬する)。
  • **非同期(コールバック)**のエラー → 外側の try では捕まらないことがある。コールバック内で try、または Promise/.catch、async/await を使おう。
  • 実際に手を動かして、上の例をブラウザのコンソールや Node.js で試すと理解が早いです。
タイトルとURLをコピーしました