10.3 try-catch-finally によるエラーハンドリング

async/await を使用している時にエラーが発生した場合(例えば API 通信に失敗した時など)は、JavaScript の標準的なエラー処理である try-catch-finally 文を使用します。JavaScript における try-catch-finally は、単なる例外処理だけでなく、async/await を使った非同期処理の制御フロー(成功/失敗)を記述する標準的な手段として頻繁に使用されます。

基本的な構造(try, catch, finally, throw)は Java と共通していますが、以下の点に注意が必要です。

検査例外(Checked Exception)が存在しない

Java の throws IOException のように、メソッド定義で例外を宣言する必要はありません。
JavaScript の例外はすべて、Java でいう「非検査例外(Runtime Exception)」のように振る舞います。コンパイラによる例外処理の強制がないため、開発者が意識的にエラーハンドリングを実装する必要があります。

catch ブロックの引数に「型」がない

Java では catch (NullPointerException e) のように型を指定して分岐させますが、JavaScript は動的型付け言語であるため、catch (error) のように変数を一つ受け取るだけです。

また、error には Error オブジェクトだけでなく、文字列や数値などあらゆる型が投げられる可能性があります。(ただし、慣習として throw new Error(“エラーメッセージ”) を使うのが一般的です)

もし、型による分岐を行いたい場合は、catch ブロック内で if (error instanceof TypeError) のように判定ロジックを書きます(instanceof については 7 章で学習しました)。

10.3.1 try-catch-finally を使用したプログラム

JavaScript の try-catch-finally を用いた非同期処理のエラー処理を確認しましょう。

ソースフォルダ:public/ch10

ファイル名:try-catch.js

➢ try-catch.html

前の項で作った ch10/async-await.html をコピーし、ch10 にペーストしてください。
その際に、ファイル名を try-catch.html に変更します。
ファイル内の async-await.html や async-await.js の記述も、try-catch.html や try-catch.js に修正します。

➢ try-catch.js

// try-catch.js

async function fetchTask() {
    try {
        alert("通信を開始します…");

        // 練習用にエラーを発生させます
        throw new Error("サーバーが見つかりませんでした。");

        alert("ここは実行されません。");
    } catch (error) {
        // エラーが発生すると、直ちにここへジャンプします
        alert(`エラーをキャッチしました: ${error.message}`);
    } finally {
        alert("(通信処理を終了します)");
    }
}

fetchTask();

実行結果

解説

4 行目の try は、Java でもおなじみの記述です。7 行目で例外を投げる時に JavaScript 特有の Error オブジェクトを new キーワードで生成します。Error オブジェクトには一般的な Error()の他に、複数の種類のエラーオブジェクトが存在します。詳しくは次項で確認しましょう。

7: throw new Error("サーバーが見つかりませんでした。");

10.3.2 主なエラーオブジェクトの種類

JavaScript には、エラーの種類に応じた複数のエラーオブジェクトが用意されています。Java の例外クラス階層と同様に、すべて基底の Error を継承しています。

実務でよく遭遇するのは TypeError と ReferenceError です。

TypeError の典型例として、API 通信でデータが取得できなかった場合に null や undefined に対してプロパティアクセスをしてしまうケースがあります。

// API のレスポンスが null だった場合
const user = null;
console.log(user.name); // TypeError: Cannot read properties of null

ReferenceError はスペルミスなど、宣言していない変数を使おうとした際に発生します。

console.log(userName); // ReferenceError: userName is not defined
// (正しくは name で宣言していた場合など)

エラーオブジェクトが持つプロパティ

どのエラーオブジェクトも、以下のプロパティを持っています。

サンプルプログラムの 13 行目で error.message を使っていましたが、error.name を合わせて表示することで、エラーの種類も確認できます。

catch (error) {
    alert(error.name + ": " + error.message);
    // 例:「TypeError: Cannot read properties of null」
}

instanceof を使ったエラーの種類判定(復習)

catch ブロックでは、7 章で学んだ instanceof を使うことでエラーの種類ごとに処理を分岐できます。

catch (error) {
    if (error instanceof TypeError) {
        alert("型のエラー: データの形式を確認してください");
    } else if (error instanceof ReferenceError) {
        alert("変数のエラー: 変数名を確認してください");
    } else {
        alert(`エラーが発生しました: ${error.message}`);
    }
}