错误处理

从 promises 抛出的错误由传递给 then 的第二个参数(reject)或传递给 catch 的处理程序处理:

throwErrorAsync()
  .then(null, error => { /* handle error here */ });
// or
throwErrorAsync()
  .catch(error => { /* handle error here */ });

链接

如果你有一个 promise 链,那么错误将导致跳过 resolve 处理程序:

throwErrorAsync()
  .then(() => { /* never called */ })
  .catch(error => { /* handle error here */ });

这同样适用于你的 then 功能。如果 resolve 处理程序抛出异常,则将调用下一个 reject 处理程序:

doSomethingAsync()
  .then(result => { throwErrorSync(); })
  .then(() => { /* never called */ })
  .catch(error => { /* handle error from throwErrorSync() */ });

错误处理程序返回一个新的 promise,允许你继续一个 promise 链。错误处理程序返回的 promise 使用处理程序返回的值解析:

throwErrorAsync()
  .catch(error => { /* handle error here */; return result; })
  .then(result => { /* handle result here */ });

你可以通过重新抛出错误让一个错误级联一个 promise 链:

throwErrorAsync()
  .catch(error => {
      /* handle error from throwErrorAsync() */
      throw error;
  })
  .then(() => { /* will not be called if there's an error */ })
  .catch(error => { /* will get called with the same error */ });

通过将 throw 语句包装在 setTimeout 回调中,可以抛出未由 promise 承担的异常:

new Promise((resolve, reject) => {
  setTimeout(() => { throw new Error(); });
});

这是有效的,因为 promises 不能处理异步抛出的异常。

未经处理的拒绝

如果 promise 没有 catch 块或 reject 处理程序,则会默默忽略错误:

throwErrorAsync()
  .then(() => { /* will not be called */ });
// error silently ignored

为防止这种情况,请始终使用 catch 块:

throwErrorAsync()
  .then(() => { /* will not be called */ })
  .catch(error => { /* handle error*/ });
// or
throwErrorAsync()
  .then(() => { /* will not be called */ }, error => { /* handle error*/ });

或者,订阅 unhandledrejection 事件以捕获任何未处理的被拒绝的承诺:

window.addEventListener('unhandledrejection', event => {});

有些承诺可以在创建时间之后处理他们的拒绝。只要处理了这样的承诺, rejectionhandled 事件就会被触发:

window.addEventListener('unhandledrejection', event => console.log('unhandled'));
window.addEventListener('rejectionhandled', event => console.log('handled'));
var p = Promise.reject('test');

setTimeout(() => p.catch(console.log), 1000);

// Will print 'unhandled', and after one second 'test' and 'handled'

event 参数包含有关拒绝的信息。event.reason 是错误对象,event.promise 是导致事件的 promise 对象。

在 Nodejs 中,rejectionhandledunhandledrejection 事件分别在 process 上被称为 rejectionHandledunhandledRejection ,并且具有不同的签名:

process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});

reason 参数是错误对象,promise 参数是对导致事件触发的 promise 对象的引用。

应考虑使用这些 unhandledrejectionrejectionhandled 事件仅用于调试目的。通常,所有承诺都应该处理他们的拒绝。

注意: 目前,只有 Chrome 49+和 Node.js 支持 unhandledrejectionrejectionhandled 活动。

注意事项

fulfillreject 链接

then(fulfill, reject) 函数(两个参数都不是 null)具有独特而复杂的行为,除非你确切知道它是如何工作的,否则不应该使用它。

如果为其中一个输入提供 null,该函数将按预期工作:

// the following calls are equivalent
promise.then(fulfill, null) 
promise.then(fulfill)

// the following calls are also equivalent
promise.then(null, reject) 
promise.catch(reject)

但是,当给出两个输入时,它采用独特的行为:

// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.then(fulfill).catch(reject)

// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.catch(reject).then(fulfill)

then(fulfill, reject) 函数看起来像是 then(fulfill).catch(reject) 的快捷方式,但它不是,如果可以互换使用会导致问题。一个这样的问题是 reject 处理程序不处理来自 fulfill 处理程序的错误。以下是将要发生的事情:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }, // error in the fulfill handler
        error => { /* this is not called! */ });

上面的代码将导致拒绝的承诺,因为传播了错误。将它与以下代码进行比较,从而产生一个满足的承诺:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }) // error in the fulfill handler
    .catch(error => { /* handle error */ });

当使用 then(fulfill, reject)catch(reject).then(fulfill) 交替使用时,存在类似的问题,除了传播履行的承诺而不是被拒绝的承诺。

从应该返回 promise 的函数同步抛出

想象一下这样的函数:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    throw new Error('UnexpectedValue')
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

如果在 promise 链的中间使用了这样的函数,那么显然没有问题:

makeSomethingAsync().
  .then(() => foo('unexpectedValue'))
  .catch(err => console.log(err)) // <-- Error: UnexpectedValue will be caught here

但是,如果在 promise 链之外调用相同的函数,则错误将不会由它处理并将被抛出到应用程序:

foo('unexpectedValue') // <-- error will be thrown, so the application will crash
  .then(makeSomethingAsync) // <-- will not run
  .catch(err => console.log(err)) // <-- will not catch

有两种可能的解决方法:

返回带有错误的被拒绝的承诺

而不是扔,做如下:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    return Promise.reject(new Error('UnexpectedValue'))
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

将你的功能包装到承诺链中

throw 语句已经在 promise 链中时,它将被正确捕获:

function foo(arg) {
  return Promise.resolve()
    .then(() => {
      if (arg === 'unexepectedValue') {
        throw new Error('UnexpectedValue')
      }

      return new Promise(resolve => 
        setTimeout(() => resolve(arg), 1000)
      )
    })
}