← 返回题目列表

Promise.prototype.finally 有什么作用?它会改变链路结果吗?

中等 第 24 / 28 题 更新于 2026/07/29
ES6Promisefinally

简化版

Promise.prototype.finally 用于在 Promise fulfilled 或 rejected 后都执行一段清理逻辑,比如关闭 loading、释放状态、记录结束时间。

正常情况下,finally 不改变前面的成功值或失败原因;但如果 finally 自己抛错,或者返回 rejected Promise,它会用新的错误覆盖原来的链路结果。

详细版

fetchUser()
  .then(user => render(user))
  .catch(err => showError(err))
  .finally(() => hideLoading());

透传成功值:

Promise.resolve(1)
  .finally(() => 2)
  .then(value => console.log(value)); // 1

透传失败原因:

Promise.reject(new Error("A"))
  .finally(() => {})
  .catch(err => console.log(err.message)); // A

覆盖原结果:

Promise.resolve(1)
  .finally(() => Promise.reject(new Error("cleanup failed")))
  .catch(err => console.log(err.message)); // cleanup failed

完整版教学

一、finally 的设计目标

很多异步流程无论成功还是失败,都需要做同一件事。

start loading
  ├─ success -> render
  └─ failure -> show error
end loading

finally 就是给“收尾动作”留的位置。

finally 适合清理副作用,不适合生成新的业务值。

二、finally 与 then/catch 的关系

不用 finally 也能写,但会重复。

request()
  .then(value => {
    hideLoading();
    return value;
  })
  .catch(error => {
    hideLoading();
    throw error;
  });

使用 finally

request()
  .finally(() => hideLoading());

代码意图更集中。

三、正常返回时会透传原结果

Promise.resolve("ok")
  .finally(() => "ignored")
  .then(value => console.log(value)); // ok

finally 回调没有参数。它不关心前面是成功值还是失败原因。

前序状态finally 正常完成后续状态
fulfilled(value)正常fulfilled(value)
rejected(reason)正常rejected(reason)

四、finally 抛错会覆盖结果

Promise.resolve("ok")
  .finally(() => {
    throw new Error("bad cleanup");
  })
  .catch(err => console.log(err.message));

收尾逻辑失败也是失败,因此会替换原来的成功结果。

如果前面已经失败,finally 再失败,也会用新的错误覆盖旧错误。

五、finally 返回 Promise 时会等待

Promise.resolve(1)
  .finally(() => delay(100))
  .then(value => console.log(value)); // 约 100ms 后输出 1

如果返回的 Promise rejected,链路变成 rejected。

Promise.reject(new Error("A"))
  .finally(() => Promise.reject(new Error("B")))
  .catch(err => console.log(err.message)); // B

六、实际工程场景

常见场景:

  • 关闭 loading
  • 释放按钮禁用态
  • 清理临时变量
  • 记录耗时
  • 断开一次性订阅
const start = performance.now();

api.save(data)
  .finally(() => {
    metrics.timing("save", performance.now() - start);
  });

七、常见误区与追问

  • 误区:finally 的返回值会成为下一个 then 的值。 正常返回会被忽略,原值会透传。
  • 误区:finally 只能处理成功场景。 fulfilled 和 rejected 都会执行。
  • 误区:finally 里不会影响链路状态。 抛错或返回 rejected Promise 会覆盖原结果。
  • 误区:finally 可以拿到前面的 value 或 reason。 回调没有业务参数。
  • 追问:为什么 loading 适合放 finally? 因为成功和失败都需要关闭 loading。
  • 追问:finally 返回 pending Promise 会怎样? 后续链路会等待它完成。

八、加强记忆

口诀:

finally normal -> pass through
finally throws -> replace with error
finally waits  -> wait returned promise

回答时先说用途是统一清理,再说默认透传,最后补上“finally 自己失败会覆盖结果”这个高价值追问点。