错误处理
从 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 中,rejectionhandled
和 unhandledrejection
事件分别在 process
上被称为 rejectionHandled
和 unhandledRejection
,并且具有不同的签名:
process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});
reason
参数是错误对象,promise
参数是对导致事件触发的 promise 对象的引用。
应考虑使用这些 unhandledrejection
和 rejectionhandled
事件仅用于调试目的。通常,所有承诺都应该处理他们的拒绝。
注意: 目前,只有 Chrome 49+和 Node.js 支持 unhandledrejection
和 rejectionhandled
活动。
注意事项
用 fulfill
和 reject
链接
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)
)
})
}