Promise.prototype.catch()

Baseline Widely available

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

Promise 实例的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。

尝试一下

语法

js
catch(onRejected)

参数

onRejected

一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为 catch() 返回的 Promise 对象的兑现值。此函数被调用时将传入以下参数:

reason

Promise 对象的拒绝值。

返回值

返回一个新的 Promise,无论当前的 promise 状态如何,这个新的 promise 在返回时总是处于待定(pending)状态。如果调用了 onRejected,则返回的 promise 将根据此调用的返回值进行兑现,或者使用此调用引发的错误进行拒绝。如果当前的 promise 已兑现,则 onRejected 不会被调用,并且返回的 promise 具有相同的兑现值。

描述

catch 方法用于在 Promise 链进行错误处理,因为它总是会返回一个 Promise,所以它可以和 then() 方法一样被链式调用

如果一个 promise 被拒绝并且没有可调用的拒绝处理器(处理器可以是 Promise.prototype.then()Promise.prototype.catch()Promise.prototype.finally()),则拒绝事件由宿主环境来提供。在浏览器中,这将触发 unhandledrejection 事件。如果将一个处理器附加到一个已被拒绝,且已导致未处理的拒绝事件的 promise,将会触发 rejectionhandled 事件。

catch() 方法内部会调用当前 promise 对象的 then() 方法,并将 undefinedonRejected 作为参数传递给 then()。该调用的返回值直接被返回。如果你对这些方法进行封装,这一点是可以观察到的。

js
// 重写原本的 Promise.prototype.then/catch 方法,只是为了添加一些日志
((Promise) => {
  const originalThen = Promise.prototype.then;
  const originalCatch = Promise.prototype.catch;

  Promise.prototype.then = function (...args) {
    console.log("在 %o 上调用 .then 方法,参数为:%o", this, args);
    return originalThen.apply(this, args);
  };
  Promise.prototype.catch = function (...args) {
    console.error("在 %o 上调用 .catch 方法,参数为:%o", this, args);
    return originalCatch.apply(this, args);
  };
})(Promise);

// 对已经解决的 Promise 调用 catch
Promise.resolve().catch(function XXX() {});

// 输出:
// 在 Promise{} 上调用 .catch,参数为:Arguments{1} [0: function XXX()]
// 在 Promise{} 上调用 .then,参数为:Arguments{2} [0: undefined, 1: function XXX()]

这意味着传入 undefined 仍然会导致返回的 promise 被拒绝,你必须传入一个函数来防止最终的 promise 被拒绝。

因为 catch() 只是单纯的调用了 then(),它支持子类化。

备注: 下面的示例抛出了 Error 的实例。与同步的 throw 语句一样,这被认为是一种良好的实践;否则,执行捕获的部分将不得不对参数进行检查,以查看它是字符串还是错误,并且你可能会丢失有用的信息,例如堆栈跟踪。

示例

在链式调用中使用 catch() 方法

js
const p1 = new Promise((resolve, reject) => {
  resolve("成功!");
});

p1.then((value) => {
  console.log(value); // "成功!"
  throw new Error("噢,不!");
})
  .catch((e) => {
    console.error(e.message); // "噢,不!"
  })
  .then(
    () => console.log("在 catch 后,调用链恢复了"),
    () => console.log("因为有了 catch 而不会被触发"),
  );

// 下面的行为与上面相同
p1.then((value) => {
  console.log(value); // "成功!"
  return Promise.reject("噢,不!");
})
  .catch((e) => {
    console.error(e); // "噢,不!"
  })
  .then(
    () => console.log("在 catch 后,调用链恢复了"),
    () => console.log("因为有了 catch 而不会被触发"),
  );

抛出错误时的陷阱

大多数情况下,抛出错误会调用 catch() 方法:

js
const p1 = new Promise((resolve, reject) => {
  throw new Error("哦吼!");
});

p1.catch((e) => {
  console.error(e); // "哦吼!"
});

在异步函数内部抛出的错误会像未捕获的错误一样:

js
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error("未捕获的异常!");
  }, 1000);
});

p2.catch((e) => {
  console.error(e); // 永远不会被调用
});

在调用 resolve 之后抛出的错误会被忽略:

js
const p3 = new Promise((resolve, reject) => {
  resolve();
  throw new Error("Silenced Exception!");
});

p3.catch((e) => {
  console.error(e); // 这里永远不会执行
});

如果 Promise 已兑现,catch() 不会被调用

js
// 创建一个不会调用 onReject 的 Promise
const p1 = Promise.resolve("调用下一个");

const p2 = p1.catch((reason) => {
  // 这里永远不会执行
  console.error("p1 的 catch 函数被调用了!");
  console.error(reason);
});

p2.then(
  (value) => {
    console.log("下一个 Promise 的 onFulfilled 函数被调用了");
    console.log(value); // 调用下一个
  },
  (reason) => {
    console.log("下一个 Promise 的 onRejected 函数被调用了");
    console.log(reason);
  },
);

规范

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

浏览器兼容性

BCD tables only load in the browser

参见