JavaScript | 「非同期(fetch / async/await)での例外処理のコツ」と「よくある落とし穴」を実例で比較(良いコード / 悪いコード)

JavaScript JavaScript
スポンサーリンク

JavaScript の 非同期処理(fetch / async/await) の例外処理は、初心者がつまずきやすいポイントの一つです。
ここでは、よくある「落とし穴」→「なぜダメか」→「良い書き方」を、具体コードで比較しながら、ていねいに解説します。


まず前提:同期と非同期の違い

普通の try...catch は「同期的(順番に実行される)」コードには有効ですが、
非同期処理(fetch や setTimeout, Promise) の中では注意が必要です。


目標

「非同期の中で起きたエラー」をちゃんと catch で拾えるようにする


❌ 悪い例1:try...catch が効かないパターン

try {
  fetch('https://example.com/api/data')
    .then(response => {
      // ネットワークエラーなどが起きても catch されない
      throw new Error('APIの処理中に問題が発生');
    })
    .catch(err => {
      console.log('Promise内部でエラー発生:', err.message);
    });
} catch (e) {
  console.log('外側catch:', e.message);
}
JavaScript

結果

Promise内部でエラー発生: APIの処理中に問題が発生

外側の try...catch は発動しません。
なぜなら、fetch(...).then(...)非同期で動いているから
try の中のコードが実行された時点では、まだ中の処理が終わっていないためです。


✅ 良い例1:await を使って非同期を同期的に扱う

async/await を使うと、try...catch が自然に使えます。

async function loadData() {
  try {
    const response = await fetch('https://example.com/api/data');
    if (!response.ok) {
      throw new Error(`サーバーエラー: ${response.status}`);
    }
    const data = await response.json();
    console.log('取得成功:', data);
  } catch (e) {
    console.error('エラーをキャッチ:', e.message);
  }
}

loadData();
JavaScript

ポイント

  • await は「Promise が終わるまで待つ」→ つまり 同期的に見える
  • try...catch はこの「待っている間のエラー」も捕まえることができる。

❌ 悪い例2:async 関数を呼ぶ側で try...catch を忘れる

async function getUser() {
  throw new Error('ユーザー情報が見つかりません');
}

try {
  getUser(); // ← await を付けていない
} catch (e) {
  console.log('キャッチ:', e.message);
}
JavaScript

結果

Uncaught (in promise) Error: ユーザー情報が見つかりません

外側の try...catch は効きません。
理由:getUser() は Promise を返すだけで、まだエラーが発生していない(非同期中)。


✅ 良い例2:呼び出し側で await を付ける

async function getUser() {
  throw new Error('ユーザー情報が見つかりません');
}

async function main() {
  try {
    await getUser(); // ← await 付き! これでエラーを捕まえられる
  } catch (e) {
    console.log('キャッチ:', e.message);
  }
}

main();
JavaScript

await を付けると、Promise の完了(成功 or 失敗)を待つため、
try...catch が正しく機能します。


まとめ:非同期エラー処理の「悪い書き方」vs「良い書き方」

目的悪い書き方良い書き方
Promiseチェーン中のエラーtry...catch で囲む.catch() でハンドリングする
async関数内のエラー.then().catch() で処理try...catchawait を使う
async関数を呼び出す側関数() だけ呼ぶawait 関数() で呼ぶ or .catch() を付ける
同期処理と非同期処理の混在同じ try にまとめるawait を使い「順番に実行」する

実用例:fetch + async/await + エラー処理

async function fetchUser(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    if (!response.ok) {
      throw new Error(`HTTPエラー ${response.status}`);
    }

    const data = await response.json();
    console.log('ユーザー取得成功:', data);
    return data;

  } catch (err) {
    console.error('データ取得に失敗:', err.message);
    return null; // 失敗時は null など安全な値を返す
  }
}

async function main() {
  const user = await fetchUser(123);
  if (user === null) {
    console.log('再試行しますか?');
  }
}

main();
JavaScript

落とし穴まとめ

落とし穴原因対処法
try...catchfetch のエラーが取れない非同期だからawait で待つ or .catch() を付ける
async 関数を呼ぶ側で例外が無視されるawait していない呼び出し側でも await or .catch()
ネットワークエラーとHTTPエラーの混同fetch は「通信エラー」でしか reject しないif (!res.ok) で HTTP エラーを自前で検出
例外を throw したまま放置未処理Promise警告必ず try...catch or .catch()

練習問題

問題:
次の関数はエラーを正しく捕まえていません。どこを直せばいいでしょう?

async function getData() {
  const res = await fetch('https://wrong.url/api');
  const data = await res.json();
  console.log(data);
}

try {
  getData(); // ← ここが問題
} catch (e) {
  console.log('キャッチ:', e.message);
}
JavaScript

👇
解答:

try {
  await getData(); // await を付ける(呼び出し元も async にする)
} catch (e) {
  console.log('キャッチ:', e.message);
}
JavaScript

まとめ(初心者が覚えるべきポイント)

  1. await なしでは非同期エラーは捕まらない
  2. try...catchasync関数の中で使う
  3. fetch の場合は「HTTPステータス」も自分でチェックする
  4. 例外を「握りつぶさず」、適切に return or throw で扱う
タイトルとURLをコピーしました