JavaScript | try…catch…finally 文による例外処理

JavaScript
スポンサーリンク

では try…catch…finally を中心に、初心者向けに やさしく、順を追って、たっぷり例題付きで 解説します。読むだけで「実際にコードを書いてデバッグできる」レベルを目指します。


1) まずは仕組みを直感で理解する(超かんたん)

  • try:エラーが出るかもしれない処理を入れる箱。
  • catchtry の中でエラー(例外)が起きたときに実行される箱。エラー情報を受け取れる。
  • finally:例外の有無に関係なく 必ず実行 される箱。ファイルを閉じる、タイマーをクリアする、UIを戻す、などに使う。

図にすると:

実行開始
  └─ try の処理実行
        ├─ 正常なら → finally 実行 → 続行
        └─ エラーなら → catch 実行 → finally 実行 → 続行

2) 基本の書き方(コードで確認)

try {
  // エラーが出るかもしれない処理
} catch (e) {
  // エラーが発生したときの処理(e にエラー情報が入る)
} finally {
  // 例外が起きても起きなくても「必ず」実行される処理
}
JavaScript

catch(e) は省略可能(ただしエラー情報を使わないとき)。

try {
  throw new Error('問題発生');
} catch {
  console.log('エラー発生!');
}
JavaScript

3) 具体例 → ステップで解説

例1:数値の足し算(簡単)

function sum(a, b) {
  try {
    return a + b;
  } catch (e) {
    console.error('エラー:', e);
    return null;
  } finally {
    console.log('終わりました(finally)');
  }
}

console.log(sum(2, 3)); // 5 と表示、finally 実行
JavaScript

ポイント:

  • 正常時でも finally は実行される。
  • try 内で return があっても、finallyその前に 実行される(詳しくは後述)。

例2:型の不整合でエラーを起こす(意図的にcatchを使う)

function safeDivide(a, b) {
  try {
    if (typeof a !== 'number' || typeof b !== 'number') {
      throw new TypeError('数値を渡してください');
    }
    if (b === 0) {
      throw new Error('0 で割れません');
    }
    return a / b;
  } catch (e) {
    console.error('caught:', e.message);
    return null;
  } finally {
    console.log('リソース解放などの後処理を実行');
  }
}

console.log(safeDivide(10, 2)); // 5
console.log(safeDivide(10, 0)); // null, エラーメッセージ表示
console.log(safeDivide('x', 2)); // null, TypeError
JavaScript

ポイント:

  • throw で自分から例外を発生させられる(エラーを明示するのに便利)。
  • catche.messagee.namee.stack を使える。

4) finally と return の注意(初心者がハマりやすい)

try または catchreturn があっても、finally はその前に必ず実行されます。ただし finally 自体が値を返すと元の return を上書きしてしまう ので注意。

function test() {
  try {
    return 'tryの値';
  } finally {
    return 'finallyの値';
  }
}
console.log(test()); // "finallyの値"
JavaScript

finally 内で returnthrow をしないのが鉄則。副作用(ログ、リソース解放)に使うのが適切。


5) 例外オブジェクト(Error)の使い方とカスタム例外

エラーは new Error('メッセージ') で作ります。組み込みの TypeErrorRangeError もあります。必要なら自分でカスタムエラーを作れます。

class MyAppError extends Error {
  constructor(message) {
    super(message);
    this.name = 'MyAppError';
  }
}

try {
  throw new MyAppError('ユーザー定義の問題');
} catch (e) {
  console.log(e.name, e.message); // MyAppError ユーザー定義の問題
  console.log(e.stack); // スタックトレース
}
JavaScript

6) 非同期(Promise / async/await)での例外処理

同期コードと同じパターンで考えられますが、注意点あり。

Promise の場合

fetch('/api/data')
  .then(res => res.json())
  .catch(err => {
    // ネットワークや JSON 解析の失敗をここで処理
    console.error('fetch error:', err);
  });
JavaScript

async/await の場合(同期と同じ try/catch が使える)

async function load() {
  try {
    const res = await fetch('/api/data');
    const data = await res.json();
    return data;
  } catch (e) {
    console.error('読み込み失敗:', e);
    return null;
  } finally {
    console.log('fetch 完了(成功/失敗どちらでも)');
  }
}
JavaScript

ポイント:

  • await の先で例外(Promise の reject)が起きると catch に飛ぶ。
  • 非同期関数の内部で try/catch を使うとエラー処理が分かりやすくなる。

7) スタックトレース(e.stack)の見方 — デバッグの基礎

catch (e) { console.error(e.stack); } で得られる文字列は、どの関数でどの行で例外が起きたかを示します。ブラウザ・Node でフォーマットはやや違いますが、基本的には上から順に「発生場所 → 呼び出し元 → …」という流れです。
デバッグ方法:

  • console.error(e) / console.error(e.stack) を出す。
  • ブラウザ開発ツール(DevTools)の Console に表示されるスタックをクリックすると該当ソースへジャンプできる。
  • debugger; をコードに書く(あるいは DevTools のブレークポイント)で、例外発生前後をステップ実行可能。

8) よくあるよくある間違い(初心者がやりがち)

  1. finallyreturn を書いて後の値を上書きしてしまう。
  2. 無理にすべてのエラーを握りつぶしてしまう(catch 内で何もせず silent にする)。→ ログは残すべき。
  3. 非同期の Promise を返さない async 関数でエラーを外に出してしまう(await を忘れて try/catch が効かないケース)。
// NGパターン
try {
  fetch('/api'); // await を忘れた → ここでは例外は捕まらない
} catch (e) {
  // ここは実行されないことが多い
}
JavaScript

await fetch(...) あるいは fetch(...).catch(...) を使う。


9) 練習問題

下に問題と解答を用意しました。まずは自分でコードを実行してみてから答えを見てください。

問題1

parseInt を使って文字列から数を取り出す関数 toNumber(str) を作る。もし str が数字に変換できない場合は throw new Error('数値ではありません') して、それを呼び出し元で catch して "invalid" を返すようにし、finally"done" をコンソールに出力すること。

解答1(参考)

function toNumber(str) {
  try {
    const n = parseInt(str, 10);
    if (Number.isNaN(n)) throw new Error('数値ではありません');
    return n;
  } catch (e) {
    return 'invalid';
  } finally {
    console.log('done');
  }
}

console.log(toNumber('123')); // 123 と表示, done
console.log(toNumber('abc')); // invalid と表示, done
JavaScript

問題2

非同期関数 fetchJson(url) を作る。fetch して res.json() を返すが、HTTP ステータスが 200 以外なら throw して、それを catch して null を返すようにし、finally"fetch finished" を出力すること。(擬似コードで OK)

解答2(参考)

async function fetchJson(url) {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error('HTTP error ' + res.status);
    const data = await res.json();
    return data;
  } catch (e) {
    console.error(e);
    return null;
  } finally {
    console.log('fetch finished');
  }
}
JavaScript

10) デバッグ実践ワンポイント(ブラウザ開発ツール)

  1. エラーが起きたら Console に出る赤いメッセージをクリック → どのファイルの何行目か分かる。
  2. Sources(ソース)タブで該当ファイルにブレークポイントを置いてステップ実行。変数の中身を確認できる。
  3. debugger; をコードに入れると、その行で自動的に停止する(DevTools が開いている場合)。
  4. async/await の中で止めたいときはブレークポイントを await の行に置けば、その前後の値を確認できる。
  5. Source Map がある場合は TypeScript / Babel 等の元のコード(.ts / .jsx)でデバッグできる。

まとめ(覚えておくべきこと)

  • try で安全に囲む、catch で「どう処理するか」決める、finally で「必ずやること」を書く。
  • throw で自分からエラーを投げられる。new Error(...) を基本に。
  • finallyreturnthrow を書くと元の挙動が変わるので避ける。
  • 非同期(Promise / async)でも try/catch を使える。await を忘れない!
  • 常にエラーはログを残しつつ適切に処理(握りつぶさない)。
タイトルとURLをコピーしました