まず finally を一言でいうと
finally は、
「Promise が成功しても失敗しても“どっちにしても最後に必ず実行したい処理”を書く場所」です。
例えば、
- ローディング表示を消したい
- モーダルを閉じたい
- ファイルや接続を閉じたい
など、「成功でも失敗でも必ず実行したい片付け処理」を書くのに使います。
ここが重要です。then は成功時、catch は失敗時、finally は「成功でも失敗でも、とにかく最後に一回実行」 という役割だとイメージしてください。
同期の try / catch / finally と対応させてイメージする
同期コードの「finally」
まずは、同期の try / catch / finally を思い出します。
try {
console.log("1: メイン処理");
throw new Error("エラー発生");
} catch (e) {
console.log("2: エラー処理");
} finally {
console.log("3: 後片付け(必ず実行される)");
}
JavaScript結果はこうなります。
1: メイン処理
2: エラー処理
3: 後片付け(必ず実行される)
ポイントは、エラーがあってもなくてもfinally の中身は必ず実行されることです。
これが「必ず最後に実行される場所」という意味の finally です。
Promise の世界の finally
Promise でも、ほぼ同じ役割で finally が使えます。
doSomethingAsync()
.then((result) => {
console.log("成功:", result);
})
.catch((error) => {
console.log("失敗:", error);
})
.finally(() => {
console.log("最後に必ず実行される処理");
});
JavaScript成功して then が走っても、
失敗して catch が走っても、
そのあとに finally が必ず実行されます。
ここが重要です。
「try / catch / finally」の finally に当たるものが、Promise の .finally
と考えると、とても理解しやすくなります。
finally の基本的な挙動
成功・失敗どちらでも実行される
実際に成功パターンと失敗パターンで挙動を比べてみます。
成功の場合:
function successPromise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("OK");
}, 500);
});
}
successPromise()
.then((value) => {
console.log("then:", value);
})
.catch((err) => {
console.log("catch:", err);
})
.finally(() => {
console.log("finally: どっちでも最後に走る");
});
JavaScript出力イメージはこうです。
then: OK
finally: どっちでも最後に走る
失敗の場合:
function failPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("NG"));
}, 500);
});
}
failPromise()
.then((value) => {
console.log("then:", value);
})
.catch((err) => {
console.log("catch:", err.message);
})
.finally(() => {
console.log("finally: どっちでも最後に走る");
});
JavaScript出力イメージはこうです。
catch: NG
finally: どっちでも最後に走る
両方のパターンで finally が必ず実行されているのが分かると思います。
finally のコールバックは「成功・失敗の情報」を受け取らない
then や catch と違い、finally に渡す関数は引数を取りません。
promise.finally(() => {
// 引数は受け取れない(value や error はここには来ない)
});
JavaScript初心者がよくやる間違いはこれです。
promise.finally((value) => {
console.log(value); // これは常に undefined
});
JavaScriptfinally の中では「成功した値」や「エラーの中身」は受け取れません。
あくまで、「結果に関係なく実行したい処理」だけを書く場所です。
ここが重要です。
値やエラーを扱いたいのは then / catch、
finally は「後片付け専用」と割り切ると、役割がはっきりします。
finally がよく使われる場面
ローディング表示の ON / OFF
とても典型的な例です。
showLoading(); // ローディング表示 ON
fetch("/api/data")
.then((res) => res.json())
.then((data) => {
console.log("データ取得成功:", data);
})
.catch((err) => {
console.error("データ取得失敗:", err);
})
.finally(() => {
hideLoading(); // 成功でも失敗でもローディングを消す
});
JavaScriptこうしておけば、
通信が成功したときも
サーバーエラーで失敗したときも
予期せぬエラーが起きたときも
必ず最後に hideLoading() が呼ばれます。
もし finally がないと、
then の中と catch の中で、それぞれ hideLoading() を書いたり、
書き忘れたり、両方に書いて重複したりしがちです。
finally を使うことで、
「どんな結果でも必ずやること」を 1 箇所にまとめられます。
資源の開放・状態のリセット
例えば簡単な例として、「フラグを戻す」ケース。
let isBusy = false;
function doTask() {
if (isBusy) return;
isBusy = true;
return someAsyncTask()
.then((result) => {
console.log("結果:", result);
})
.catch((err) => {
console.error("エラー:", err);
})
.finally(() => {
isBusy = false; // 忙しいフラグを必ず戻す
});
}
JavaScriptここでは、
処理開始時:isBusy を true にする
どんな結果でも最後:isBusy を false に戻す
という保証ができます。
ここが重要です。
finally は、「途中で成功・失敗の分岐がいろいろあっても、必ず最後にやるべき後片付けを一箇所に書ける」ことが最大の価値 です。
finally の後ろに then / catch を続けたときの動き
finally も Promise を返す
finally 自体も、then や catch と同じで「新しい Promise」を返します。
基本ルールは次のようなイメージです。
元の Promise が成功していた → finally のあとに続く then は「成功」として動く
元の Promise が失敗していた → finally のあとに続く catch は「失敗」として動く
finally の中で特別なことをしなければ、
元の Promise の「成功 / 失敗」はそのまま次に伝わります。
例:
Promise.resolve("OK")
.finally(() => {
console.log("片付け");
})
.then((v) => {
console.log("結果:", v);
});
JavaScript出力はこうです。
片付け
結果: OK
元々成功しているので、そのまま成功として次の then に値が渡っています。
finally の中でエラーを投げるとどうなるか
finally の中でエラーが起きた場合、そのエラーが「新しい結果」になります。
Promise.resolve("OK")
.finally(() => {
throw new Error("finally でエラー");
})
.then((v) => {
console.log("これは実行されない");
})
.catch((err) => {
console.log("catch:", err.message);
});
JavaScript出力イメージ:
catch: finally でエラー
元は成功していましたが、finally 内でのエラーにより、結果は「失敗」に上書きされ、catch に流れます。
ここが少しだけ重要なポイントです。
通常は finally では「エラーを投げない」(ログや片付けだけを書く)。
もし投げるなら、「それが新しいエラーとして扱われる」ことを意識しておく必要があります。
初心者としての「finally の押さえどころ」
ここまでを、いちばんシンプルにまとめます。
Promise における finally は、「成功でも失敗でも最後に必ず実行したい処理を書く場所」。
同期の try / catch / finally における finally とほぼ同じ役割を持つ。
then は成功時、catch は失敗時、finally はそのどちらでも必ず 1 回だけ実行される。
finally のコールバックは引数を取らない(成功値やエラーはここでは受け取れない)。
典型的な用途は、「ローディング表示の ON/OFF」「フラグや状態のリセット」「リソースの解放(接続を閉じるなど)」。
finally のあとにも then / catch をつなげられ、通常は元の成功・失敗をそのまま次に伝える。
ただし、finally の中でエラーを投げると、そのエラーが新しい失敗として扱われる。
ここが重要です。
finally を「エラーかどうかに関係なく絶対やる後片付けを書く場所」として頭に固定しておくと、
Promise のコードに“安心感”が生まれます。
練習としては、次のような小さなコードを書いてみるといいです。
成功する Promise + finally
失敗する Promise + finally
成功・失敗どちらのときも、finally が必ず実行されるか確認する
それを一度体感すると、
「成功は then」「失敗は catch」「最後の後片付けは finally」という三役の役割分担が、
とてもクリアに感じられるはずです。
