はじめに
例外処理を実装するにあたって欠かせないのがtry-catch
文です。これを使うことで、ランタイムエラーが発生しても処理を継続できたりするわけです。
ところでtry-catch
文ではfinally
というものを使うこともできます。これはtry
ブロックの中が正常に完了しても、catch
に進んだとしても、最後に実行される処理を書く場所です。
try {
// 何らかの処理
console.log('tryで実行');
} catch {
// エラー次の処理
console.log('catchで実行');
} finally {
// 最後に必ず行う処理
console.log('最後に必ず実行');
}
ところで、わざわざfinally
って使う必要ありますか?
try
完了後(あるいはcatch
完了後)に何かの処理を実行させたいなら、以下のように書けばいいはずです。
try {
// 何らかの処理
console.log('tryで実行');
} catch {
// エラー次の処理
console.log('catchで実行');
}
// 最後に必ず行う処理
console.log('最後に必ず実行');
わざわざfinally
が用意されているのはなぜでしょうか。
例外が持つ大域脱出
実は、脱出に割り込むことはfinally
にしかできません。
try-catch
文は例外処理と合わせて使われることが多いですが、例外は大域脱出という特徴を持っています。大域脱出は、その場でプログラムの実行を中断して別の場所にプログラムの制御を移すことを指します。
try {
throwError();
// throwError()で例外が投げられてたので、これは実行されない
console.log('実行されない');
} catch (error) {
console.log(error);
}
function throwError () {
const error = new Error('エラーが発生しました');
throw error;
// 例外が投げられたので、これは実行されない
console.log('実行されない');
}
大域脱出には、複数箇所で発生した例外を1箇所で処理できるというメリットがあります。
finallyだけができること
この脱出にfinally
は割り込むことができます。
try {
// 何らかの処理
console.log('tryで実行');
throwError();
} finally {
// エラー次の処理
console.log('最後に実行');
}
上記のコードを実行すると、以下のような結果になります。
tryで実行
最後に実行
Uncaught Error: エラーが発生しました
at throwError (<anonymous>:2:16)
at <anonymous>:9:2
try
→finally
という順に処理は進むはずなので、順番的にはUncaught Error
の後に「最後に実行」が出力されそう(正確にはその前にランタイムエラーが発生しているので出力はされない)ですが、実際にはこのような結果になります。
これは、try
文から脱出する際にfinally
が実行されたことを意味しています。
このように、finally
は脱出に割り込むことができるという特性を持っています。
最後に
とはいえfinally
を使う機会はあまりないと思います。