錯誤處理
從 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)
)
})
}