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) を作り、text は JSON の文字列として受け取る
- 戻り値は { ok, works, errorName, finished } の 4 つのキーを持つオブジェクトにする
- JSON.parse に成功し、結果が配列なら ok を true、works を解析結果にする
- 解析に失敗した場合と結果が配列でない場合は ok を false、works を空配列、errorName に error.name を入れる
- finished は成功でも失敗でも true にする。finally を使うこと
入出力例
safeParseWorks("[{"id":1,"title":"プロフィールサイト","category":"site","views":120},{"id":2,"title":"自己紹介カード","category":"card","views":48}]") → {"errorName":null,"finished":true,"ok":true,"works":[{"category":"site","id":1,"title":"プロフィールサイト","views":120},{"category":"card","id":2,"title":"自己紹介カード","views":48}]}
safeParseWorks("[]") → {"errorName":null,"finished":true,"ok":true,"works":[]}
safeParseWorks("[{"id":1,"title":"プロフィールサイト"") → {"errorName":"SyntaxError","finished":true,"ok":false,"works":[]}
safeParseWorks("") → {"errorName":"SyntaxError","finished":true,"ok":false,"works":[]}
safeParseWorks("{"id":1,"title":"学習ログ"}") → {"errorName":"TypeError","finished":true,"ok":false,"works":[]}