Error handling¶
Error handling, try
… catch
¶
<https://javascript.info/try-catch> のノート。
ここで習う構文を使えば、エラーを捕まえて、スクリプトが死ぬのではなく、より合理的な処理をすることができる。
The try
… catch
syntax¶
ほかのプログラミング言語にも似たものがある:
try {
// code...
} catch (err) {
// error handling
}
Error object¶
すべての組み込みエラーオブジェクトには次のプロパティーがある:
Property |
Description |
---|---|
|
|
|
エラーの詳細を述べる文字列 |
Optional catch
binding¶
catch
節でエラーを参照しないつもりならば、次のように書ける:
try {
// code...
} catch {
// error handling
}
Using try
… catch
¶
本文では次のコードを例示している。
let json = "{ bad json }";
try {
let user = JSON.parse(json);
// ...
} catch (err) {
alert("Our apologies, the data has errors, we'll try to request it one more time.");
alert(err.name);
alert(err.message);
}
ここでの catch
節での対応はエラーメッセージを出力するだけの単純なものだ。実際には、新しいネットワークリクエストを送る、訪問者に代替手段を提案する、エラーに関する情報をログ機能に送る、などの処理が考えられる。
Throwing our own errors¶
アプリケーション固有のエラーを考える。
Throw
operator¶
演算子 throw
はエラーを送出する。オペランドにエラーを表現するオブジェクトをとる。
エラーオブジェクトは何でも送出することができる。組み込みエラーに倣って、
name
と message
くらいは持たせるのがいい。
JavaScript には組み込みエラーがたくさんある。状況に合致すればそれらを利用してもいい。
let error = new Error(message);
// or = new SyntaxError(message);
// or = new ReferenceError(message);
これらの組み込みエラーにおいては name
はコンストラクターの名前となる。
JSON.parse()
ででたらめな文字列を渡すとSyntaxError
が送出されることを見る。JSON.parse()
が成功し、戻り値のオブジェクトにアプリケーションが期待するプロパティーが含まれていない場合、throw
文で固有の例外を送出する。
どちらのエラーも catch
節一つで処理していることに注意する。
Rethrowing¶
catch
節ではアプリケーションが処理できるエラーしか扱いたくない。処理できるかを判定するのにエラーオブジェクトをテストする。処理できないエラーは再送出する。
ここでは演算子
instanceof
を使って、エラーの型をテストしている。再送出は
throw
文に捕まえたオブジェクトをそのまま渡すことで行う。
どの catch
ブロックにも処理されなかったエラーが最終的に生じると、スクリプトは殺される。
try
… catch
… finally
¶
JavaScript にも finally
節がある。意味も他のプログラミング言語のそれと同じだ。
try
節に return
文など、ブロック外へ脱出する命令がある場合にも
finally
節の内容は脱出直前に実行される。
try
節と finally
節があれば、そのエラー処理での catch
節は書かなくても文法的には問題ない。
この記事では finally
節で例外を送出するケースについて述べられていない。何か欲しい。
Global catch
¶
仕様にはないが、環境は最終的にエラーを処理するための機能を用意している。
Node.js では
process.on("uncaughtException")
がそのために用意されている。ブラウザーでは、特別プロパティー
window.onerror
に関数を割り当てて、未処理エラーに対して実行させることができる。
もっとも、アプリケーションで処理し切れなかったエラーがこの機能で満足に処理できるという場合はまずないだろう。せいぜい確認用だ。
Tasks¶
Finally or just the code?¶
もう一つ指摘するとすれば、catch
節で再送出する場合の後始末も対応できることか。
Custom errors, extending Error¶
<https://javascript.info/custom-errors> のノート。
JavaScript では throw
を任意のオペランドで使用できる。技術的には、独自のエラークラスは Error
を継承する必要はない。しかし、継承することでエラーオブジェクトを識別するために obj instanceof Error
を使用することが可能になる。したがって、Error
を継承するのがよい。
アプリケーションが大きくなると、自ずとエラーが階層化される。例えば
HttpTimeoutError
は HttpError
を継承する、という具合だ。
Extending Error
¶
関数 readUser(json)
を自作する状況で、入力にユーザープロパティーが含まれていない場合のエラーをも自作することを考える。そのエラーの定義は、組み込みエラー
Error
から継承することにすると、さしあたり次のようなものになる:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
Error
のコンストラクターはmessage
しか受け付けないので、サブクラスでthis.name
を上書きする。
readUser(json)
の呼び出し例は次のようになる。他の言語と比べて catch
節の書き方が面倒であることに気づく:
try {
let user = readUser('{ "age": 25 }');
// ...
} catch (err) {
if (err instanceof ValidationError) {
alert("Invalid data: " + err.message);
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err;
}
}
JavaScript の catch
節は throw
されたすべてのエラーを捕捉するため、エラータイプによる場合分けを、上のようにより特殊なものから判定していくことになる。
Further inheritance¶
ValidationError
は汎用エラーとして、もっと特殊なエラーを定義することにする。そのようなエラーを ValidationError
から継承して定義する。
基底クラスのコンストラクターでの
this.name
のセットにコツがある。
Wrapping exceptions¶
今度は関数 readUser(json)
が送出するエラーを ReadError
に一本化する。関数内部に設けてある try
節から何らかのエラーが送出された場合、catch
節でそのエラーオブジェクトをこの新しいエラー型オブジェクトに持たせる。それから、この新しいエラーオブジェクトを送出して終わる。
この節のエラー処理に関する議論は美しい。本文のクラスを図式化するとこういう感じになる:
Tasks¶
Inherit from SyntaxError¶
組み込みエラーから自作エラーを定義する作法を確認する問題。