JavaScriptの例外処理とエラーの書き方

JavaScript

JavaScriptの例外処理とエラーの書き方についてまとめました。

例外処理とエラーの書き方

例外処理(Exception Handling)とは、エラーが発生したときに飛ぶ特別な処理のことです。
try{}→catch{}→finally{} で処理されます。

try {
  本来動かしたい処理を書く
  throw new Error()
    try の中の以下のコードは実行されず、
    catch()に移行する
  } catch(e) {
    エラーの処理
  } finally {
    終了の処理
    try でも catch でも実行される
  }

例外処理のシンプルな例文

try {
  console.log('start');
  throw new Error(`エラーが発生しました`);
  console.log('end');
} catch (e) {
  console.log(e);
} finally {
  console.log('終了');
}
//start
//Error: エラーが発生しました
//終了

try{}の中のthrow new Error()以下は実行されずに、catch{}に移行しているのがわかります。
実際は、throw new Error()は別の関数に記述することが多いです。
次のようなケースをみてみましょう。

例外処理の具体例

次のasync/awaitの例文に例外処理を記述してみます。

//userデータをとってくる
async function fetchData() {
  const response = await fetch('users.json');
  const json = await response.json();
  return json;
}
//データを加工する
async function init() {
  const users = await fetchData(); //ここでエラーが投げられる
  for (const user of users) {
    console.log(`私は${user.name}です。${user.age}才です。`);
  }
}
// 実行する
init();

まず、上のようにデータを取得する処理とデータを加工する処理を分けます。
そして、データを取得するときにエラーがあった場合、加工する前にエラーメッセージを投げたいとします。
つまり、throw new Error()を記述するのは、データをとってくる処理のところになります。

エラーを出すために、たとえばjson.lengthが0のときにエラーを投げる処理とします。jsonファイルのデータを消すとcatch{}が反応します。

そして、try{}の部分で本来行いたい処理を記述します。
その中で、データを加工する前にエラーをエラーを投げられるようにします。(await fetchData();の部分)

//userデータをとってくる
async function fetchData() {
  const response = await fetch('users.json');
  if (response.ok) {
    const json = await response.json();
    if (!json.length) {
      throw new Error('データがありません');
    }
    return json;
  }
}
//データを加工する
async function init() {
  try {
    const users = await fetchData(); //ここでエラーが投げられる
    for (const user of users) {
      console.log(`私は${user.name}です。${user.age}才です。`);
    }
  } catch (e) {
    console.log(e);
  } finally {
    console.log('終了します。');
  }
}
// 実行する
init();
// データがありません
// 終了します。

そして、エラーが投げられたときに、catch{}に移行します。
このように、throw new Error()はエラーを投げるだけにすることで、エラーの処理はcatch{}内で書き分けることが可能になります。

Promiseをより直感的に記述できるasync/awaitに関しては次の記事を参考にしてみてください。


fetch()メソッドについては、次の記事を参考にしてみてください。

カスタムエラー

Errorというクラスを継承して、独自のエラー文を返すことができるようになります。

//userデータをとってくる
async function fetchData() {
  const response = await fetch('users.json');
  if (response.ok) {
    const json = await response.json();
    if (!json.length) {
      throw new NoDataError('データがありません');
    }
    return json;
  }
}
class NoDataError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NoDataError'; //nameを設定しないとそのままErrorがでる
  }
}
// NoDataError: データがありません

nameを設定しないとそのまま’Error’がでてしまいます。(ex. Error: データがありません)
throwでエラーが投げられたとき、try{}catch{}でハンドリングしないとそれ移行の処理が行われないため、throwを使うときは必ず例外処理のハンドリングを行いましょう。

例外処理の中で条件分岐をする際に、このカスタムエラーが役に立ちます。
例外処理の中で条件分岐をするというのは、たとえばcatch{}の中で次のようにします。

//データを加工する
async function init() {
  try {
    const users = await fetchData(); //ここでエラーが投げられる
    for (const user of users) {
      console.log(`私は${user.name}です。${user.age}才です。`);
    }
  } catch (e) {
    if (e instanceof NoDataError) {
      console.log(e);
    } else {
      console.log('処理できませんでした。');
    }
  } finally {
    console.log('終了します。');
  }
}
// 実行する
init();

このようにすると、データがない場合は、「データがありません」、それ以外のエラーの場合は、「処理できませんでした。」とエラーのメッセージを分けることができます。