DrillLab
第 42 / 105 道42 / 105 · #309

Promise 链里的 finally() 有什么用

What is the purpose of the finally() method in a Promise chain

先自己答,再往下看Answer it yourself first

一句话:无论成功还是失败都会执行, 用来做收尾 —— 关 loading、 释放资源、上报耗时。

三个性质要说清:

  • 拿不到值也拿不到错误—— 回调不接参数。它的定位就是「不关心结果的清理」。
  • 透传—— 它把原来的值或错误原样往下传, 不影响链的状态。 所以 finallyreturn一个值不会改变结果
  • 但它里面抛错会覆盖原来的结果—— 这是唯一能改变链状态的方式。

为什么这题值得问:因为它对应一个 真实 bug —— 只在成功路径里setLoading(false), 出错时界面就永远卡在 Loading。我们那道 fetch 变式题的常见错误里就有这一条。

会追问:「和 try/catch/finallyfinally 一样吗?」—— 语义一样,都是「一定执行」。async/await 里直接用try/finally 就行,不用.finally()

In one line: it runs whether the promise succeeded or failed, so it is where the cleanup goes — turn off loading, release resources, report how long it took.

Three properties to state clearly:

  • It sees neither the value nor the error — the callback takes no arguments. Its whole job is cleanup that does not care about the result.
  • It passes through — the original value or error continues down the chain unchanged, so the state of the chain is untouched. A return inside finally changes nothing.
  • But throwing inside it does override the result — that is the only way it can change the chain.

Why this question earns its place: it maps onto a real bug — call setLoading(false) only on the success path and the UI sits on Loading forever when the request fails. That exact mistake is on the common-errors list for our fetch variant question.

Follow-up: “Is it the same as the finally in try/catch/finally?” — same meaning: it always runs. With async/await just use try/finally; you do not need .finally().

JavaScriptfinally 的三个性质Three properties of finally示意Illustrative
1fetch(url)
2 .then((r) => r.json())
3 .then(setData)
4 .catch(setError)
5 .finally(() => setLoading(false)); // 成功失败都要关 loading
6
7// 等价的 async 写法
8try {
9 const r = await fetch(url);
10 setData(await r.json());
11} catch (e) {
12 setError(e.message);
13} finally {
14 setLoading(false); // ← 放这里,别只放在 try 末尾
15}
16
17// finally 透传,return 不生效
18Promise.resolve(1).finally(() => 99).then(console.log); // 1,不是 99
19// 但抛错会覆盖
20Promise.resolve(1).finally(() => { throw new Error("x"); }).catch(e => console.log(e.message)); // "x"
1fetch(url)
2 .then((r) => r.json())
3 .then(setData)
4 .catch(setError)
5 .finally(() => setLoading(false)); // turn loading off whether it worked or not
6
7// The same thing written with async
8try {
9 const r = await fetch(url);
10 setData(await r.json());
11} catch (e) {
12 setError(e.message);
13} finally {
14 setLoading(false); // ← put it here, not only at the end of try
15}
16
17// finally passes the value through; its return value is ignored
18Promise.resolve(1).finally(() => 99).then(console.log); // 1, not 99
19// But throwing does replace it
20Promise.resolve(1).finally(() => { throw new Error("x"); }).catch(e => console.log(e.message)); // "x"