JavaScript | 例外処理(try…catch) の練習問題セット(初心者向け)

JavaScript
スポンサーリンク

目的

try…catch の基本、例外の伝搬(同期)、非同期コールバックでの落とし穴、Promise / async–await での例外処理を確実に理解すること。

各問題は「問題 → 予想 → 実行例(出力) → 解説(ステップごと)」の順で示します。ブラウザの開発者コンソールか Node.js で実行して確認してください。


問題 1 — 同期:例外の伝搬(基本)

問題コード

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

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

まず予想してみよう:どこで例外が捕まる?after a() は表示される?

実行結果(期待)

caught: boom from c
end

解説(ステップ)

  1. try の中で a() を呼ぶ → a()b() を呼ぶ → b()c() を呼ぶ。
  2. c()throw する → 例外は呼び出しスタックをさかのぼって(伝搬して)いく。
  3. 途中に try…catch があるので、そこで捕まり catch ブロックが実行される。
  4. after a()a() の実行が完全に成功した場合にのみ実行されるため、表示されない。
  5. その後 console.log('end') が実行される(プログラムは停止しない)。

問題 2 — 非同期コールバック:外側の try は効くか?

問題コード

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

まず予想してみようcaught outer: が出るか?after try は出るか?

実行結果(想定)

after try
// 1秒未満で以下のようなブラウザのエラーメッセージ(外側 catch では捕まらない):
// Uncaught Error: later boom

解説(ステップ)

  1. setTimeout の呼び出し自体は同期的にすぐ終わる(コールバックは後で実行)。
  2. try ブロックは setTimeout の呼び出しを囲んでいるが、コールバック関数が実際に実行されるのは try が既に終わった後。
  3. よって、コールバック内の throw は外側の catch では捕まらない。
  4. 対処法:コールバックの中で try…catch を使う、または Promise 化して .catch() する。

問題 3 — 対処:コールバック内で try…catch

問題コード

setTimeout(() => {
  try {
    throw new Error('handled inside callback');
  } catch (e) {
    console.log('caught inside callback:', e.message);
  }
}, 50);
console.log('after scheduling');
JavaScript

予想caught inside callback: が出る?after scheduling は早く出る?

実行結果(想定)

after scheduling
caught inside callback: handled inside callback

解説

  1. コールバックの中に try…catch を置くことで、非同期に発生する例外をその場で処理できる。
  2. 外側の try に頼るより確実。

問題 4 — Promise 基本:reject を .catch() で受ける

問題コード

function asyncTask(shouldFail) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldFail) reject(new Error('promise failed'));
      else resolve('ok');
    }, 50);
  });
}

asyncTask(true)
  .then(result => console.log('result:', result))
  .catch(err => console.log('promise caught:', err.message));
JavaScript

予想promise caught: が出る?

実行結果(想定)

promise caught: promise failed

解説

  1. Promisereject.catch() で受けられる。
  2. Promise ベースなら非同期エラーを外側で一箇所に集めやすい。

問題 5 — async/await と try/catch

問題コード

async function run() {
  try {
    const result = await asyncTask(true); // asyncTask は上と同じ
    console.log('result:', result);
  } catch (e) {
    console.log('async/await caught:', e.message);
  }
}
run();
JavaScript

予想:どの catch が動く?

実行結果(想定)

async/await caught: promise failed

解説

  1. await すると、Promise が reject した場合は throw が発生したように扱われる。
  2. try…catch で囲むと通常の例外と同じように捕まえられるため、同期風に書けて分かりやすい。

問題 6 — 混合:同期と非同期のエラー処理の違いを理解する

問題コード

function syncThrow() {
  throw new Error('sync error');
}
function scheduleAsyncThrow() {
  setTimeout(() => { throw new Error('async error'); }, 10);
}

try {
  syncThrow();
  scheduleAsyncThrow();
  console.log('after calls');
} catch (e) {
  console.log('caught outer:', e.message);
}
JavaScript

予想:どのエラーが caught outer に捕まる?after calls は出る?

実行結果(想定)

caught outer: sync error
// 'async error' はブラウザ/Nodeの未捕捉エラーとして表示される(outerでは捕まらない)

解説

  1. syncThrow() が即座に throw するため、try の中で捕まる。
  2. scheduleAsyncThrow()throw は後で実行されるため外側の try では捕まらない。
  3. after callssyncThrow()throw によって実行されない(try を抜けて catch に入るため)。

問題 7 — 実践:非同期処理を Promise 化してまとめてエラーハンドリング

問題コード

function task1() {
  return new Promise((res) => setTimeout(() => res('t1 ok'), 30));
}
function task2() {
  return new Promise((_, rej) => setTimeout(() => rej(new Error('t2 fail')), 60));
}
async function main() {
  try {
    const r1 = await task1();
    console.log(r1);
    const r2 = await task2(); // ここで拒否される
    console.log(r2);
  } catch (e) {
    console.log('main caught:', e.message);
  } finally {
    console.log('cleanup if needed');
  }
}
main();
JavaScript

予想:出力の順とどこで捕まる?

実行結果(想定)

t1 ok
main caught: t2 fail
cleanup if needed

解説

  1. task1 が成功 → t1 ok を出力。
  2. task2 が拒否され await が例外を投げる → catch で受ける。
  3. finally は成功/失敗に関わらず実行される(リソース解放などに使う)。

問題 8 — 演習:あなたが修正者(バグ修正)になる

問題(バグのあるコード)

function readFileSim() {
  setTimeout(() => {
    // ファイル読み取り処理のシミュレーション
    throw new Error('file read failed');
  }, 100);
}

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

課題:このコードはファイル読み取りエラーを捕まえられていません。どこをどう直せばいい?(修正案を2つ示してください)

回答(修正案)

  • 案 A:コールバック内で try…catch
function readFileSim() {
  setTimeout(() => {
    try {
      throw new Error('file read failed');
    } catch (e) {
      console.log('handled inside readFileSim:', e.message);
    }
  }, 100);
}
readFileSim();
JavaScript
  • 案 B:Promise にして呼び出し側で catch
function readFileSim() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('file read failed'));
    }, 100);
  });
}

readFileSim()
  .catch(e => console.log('caught by caller:', e.message));
JavaScript

解説

  • 元コードは setTimeout 内の throw を外側の try で捕まえられない典型。
  • コールバック内でハンドリングするか、Promise に変えて呼び出し側で .catch() / await + try…catch を使う。

最後に:練習のコツ

  • まず「この処理は同期か非同期か?」を自問する習慣をつける。
  • 小さな例(上の問題)をコピペして、自分で throw の場所を変えたり try の位置を変えたりして、挙動を確認する。
  • async/await を使うとエラーハンドリングが読みやすくなることが多い。Promise の .catch() も忘れずに。
  • ブラウザのコンソールや Node.js で実行すると、未捕捉エラー(Uncaught Error)の挙動がよく見えて学びになります。
タイトルとURLをコピーしました