try catch finally
このレッスンで分かること
tryで囲んだ範囲の失敗をcatchが受け取り、その先の処理が止まらなくなりますfinallyは成功でも失敗でも必ず走るので、後始末の置き場所になります
1 行の失敗で、下の処理がまとめて止まる
入門で扱った作品データは、自分の手で配列に書いたものでした。自分で書いた配列は形が崩れないので、失敗を考える必要がありませんでした。
実践では、作品データが文字列で外から届きます。途中で切れていることも、配列ではないものが来ることもあります。何も手当てをしていないと、その 1 行で例外が飛びます。
JavaScript
const text = '[{"id":1,"title":"プロフィールサイト"';
const works = JSON.parse(text);
console.log("ここには来ない");例外が飛ぶと、その関数の残りは実行されません。呼び出し元へさかのぼりながら受け止める場所を探し、どこにも無ければページの JavaScript がそこで止まります。作品一覧が出ないだけのつもりが、あいさつも時計もフォームも動かない画面になります。
try で囲み、catch で受け取る
try { } の中で起きた例外は、すぐ下の catch (error) { } が受け取ります。受け取った時点で例外は止まり、catch を抜けたあとの行はふつうに動きます。
JavaScript
try {
const works = JSON.parse(text);
console.log(works.length);
} catch (error) {
console.log(error.name); // SyntaxError
}
console.log("ここには来る");受け取る error は Error オブジェクトです。中身の使い分けは次のとおりです。
| プロパティ | 中身 | 分岐に使ってよいか |
|---|---|---|
name | エラーの種類の名前 | 使ってよい |
message | 人が読むための説明文 | 使わない |
stack | どこで起きたかの経路 | 使わない |
message の文言は処理系によって違います。同じ壊れた JSON でも、ある環境では「Unexpected end of JSON input」で、別の環境では「JSON Parse error」です。error.message === "..." で分岐を書くと、書いた環境でだけ動くコードになります。種類で分けたいときは name を見ます。
自分で throw した例外も、同じ catch が受け取ります。JSON としては読めるのに中身が期待と違う、という失敗はこれで扱えます。
JavaScript
try {
const count = Number("たくさん");
if (Number.isNaN(count)) {
throw new TypeError("件数が数値ではありません");
}
} catch (error) {
console.log(error.name); // TypeError
}finally は必ず走る
finally { } は、try が最後まで通っても catch に落ちても、必ず走ります。読み込み中の表示を消す、押せなくしたボタンを戻すといった、結果に関係なくやることをここに置きます。
JavaScript
function run(text) {
try {
console.log("1 try");
return JSON.parse(text);
} catch (error) {
console.log("2 catch");
return null;
} finally {
console.log("3 finally");
}
}run("[]") は 1、3 の順、run("こわれた") は 1、2、3 の順で出ます。try の中に return があっても、値を返す直前に finally が走ります。だから途中で返す関数の後始末は finally に置くのが確実です。同じ後始末を try の末尾と catch の末尾に 2 回書くと、あとで片方だけ直して片方が残る事故が起きます。
よくある間違い
- 失敗する行が
tryの外にある — 囲むのは失敗しうる行です。JSON.parseを外に出したまま、その下の行だけを囲んでも受け止められません。逆に囲みすぎもよくありません。関係のない処理まで入れると、どこで失敗したのかがcatchから分からなくなります catchを空にする — 画面は止まらなくなりますが、なぜ何も出ないのかが誰にも分からなくなります。動いているように見えて、失敗したという事実だけが消えます。受け取ったら必ずどこかに残します
要件
- safeParseWorks(text) の try 内で readWorksArray(text) の結果を result.works に入れ、その後で result.ok を true にする
- catch では捕まえた error の name を result.errorName に入れる。ok と works の初期値は変えない
- finally で result.finished を true にする。成功でも失敗でも同じ処理を通す
- 戻り値の整形と readWorksArray は準備済みなので変更しない