Promise.prototype.catch()

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

尝试一下

语法

js

catch(onRejected)

参数

onRejected

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

reason

Promise 对象的拒绝值。

返回值

返回一个新的 Promise,无论当前的 promise 状态如何,这个新的 promise 在返回时总是处于待定状态。如果 onRejected 方法抛出了一个错误或者返回了一个被拒绝的 promise,那么这个新的 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

参见