Promise.prototype.finally()

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since October 2018.

Promise 实例的 finally() 方法用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法。

这可以让你避免在 promise 的 then()catch() 处理器中重复编写代码。

尝试一下

语法

js
finally(onFinally)

参数

onFinally

一个当 promise 敲定时异步执行的函数。它的返回值将被忽略,除非返回一个被拒绝的 promise。调用该函数时不带任何参数。

返回值

立即返回一个新的 Promise。无论当前 promise 的状态如何,此新的 promise 在返回时始终处于待定(pending)状态。如果 onFinally 抛出错误或返回被拒绝的 promise,则新的 promise 将使用该值进行拒绝。否则,新的 promise 将以与当前 promise 相同的状态敲定(settled)。

描述

如果你想在 promise 敲定时进行一些处理或者清理,无论其结果如何,那么 finally() 方法会很有用。

finally() 方法类似于调用 then(onFinally, onFinally)。然而,有几个不同之处:

  • 创建内联函数时,你可以只将其传入一次,而不是强制声明两次或为其创建变量。
  • onFinally 回调函数不接收任何参数。这种情况恰好适用于你不关心拒绝原因或兑现值的情况,因此无需提供它。
  • finally() 调用通常是透明的,不会更改原始 promise 的状态。例如:
    • Promise.resolve(2).then(() => 77, () => {}) 不同,它返回一个最终会兑现为值 77 的 promise,而 Promise.resolve(2).finally(() => 77) 返回一个最终兑现为值 2 的 promise。
    • 类似地,与 Promise.reject(3).then(() => {}, () => 88) 不同,它返回一个最终兑现为值 88 的 promise,而 Promise.reject(3).finally(() => 88) 返回一个最终以原因 3 拒绝的 promise。

备注:finally 回调函数中抛出错误(或返回被拒绝的 promise)仍会导致返回的 promise 被拒绝。例如,Promise.reject(3).finally(() => { throw 99; })Promise.reject(3).finally(() => Promise.reject(99)) 都以理由 99 拒绝返回的 promise。

catch() 一样,finally() 在内部调用其调用对象上的 then 方法。如果 onFinally 不是函数,则调用 then() 时使用 onFinally 同时作为两个参数——对于 Promise.prototype.then(),这意味着没有附加有效的处理器。否则,then() 被调用时会使用两个内部创建的函数,其行为如下:

警告: 这只是为了演示,而不是一个 polyfill。

js
promise.then(
  (value) => Promise.resolve(onFinally()).then(() => value),
  (reason) =>
    Promise.resolve(onFinally()).then(() => {
      throw reason;
    }),
);

因为 finally() 调用 then(),所以它支持子类化。此外,请注意上面的 Promise.resolve() 调用——实际上,onFinally() 的返回值是使用与 Promise.resolve() 相同的算法解决的,但用于构造解决的 promise 的实际构造函数将是子类。finally() 通过 promise.constructor[Symbol.species] 获取构造函数。

示例

使用 finally()

js
let isLoading = true;

fetch(myRequest)
  .then((response) => {
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then((json) => {
    /* 进一步处理 JSON */
  })
  .catch((error) => {
    console.error(error); // 这行代码也可能抛出错误,例如:when console = {}
  })
  .finally(() => {
    isLoading = false;
  });

规范

Specification
ECMAScript Language Specification
# sec-promise.prototype.finally

浏览器兼容性

BCD tables only load in the browser

参见