promise 鏈中出現錯誤時,會順著鏈接去到最近的錯誤處理函式。也可以說最簡單的處理方式就是在鏈接的尾端加上 catch
,來抓所有錯誤,很類似一般的 try...catch
結構:
doSomething()
.then((result) => doSomethingElse(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
promise executor 或各處理函式中只要出現錯誤,無論是 promise 本身失敗、丟出例外或程式錯誤,都會有如同 rejected promise
的表現,可以在 catch
中接到處理。例如以下兩段會得到一樣的錯誤:
// 1
new Promise((resolve, reject) => {
throw new Error('Whoops!');
}).catch(alert); // Error: Whoops!
// 2
new Promise((resolve, reject) => {
reject(new Error('Whoops!'));
}).catch(alert); // Error: Whoops!
如果沒有處理,發生錯誤會造成程式中斷。這時候瀏覽器的 JS 引擎會產生一個全域錯誤,可以用 'unhandledrejection'
事件追蹤到,這段有更詳細的範例解說。
程式碼要不要巢狀撰寫除了可讀性和個人習慣外,也可以考慮這種結構會造成的實際影響。這部分可參考前一篇最後一個例子。
巢狀的一個效果是會限制 catch
的作用範圍,它變成只會抓到內層的錯誤,善加運用這點可以做到更精確的錯誤處理:
doSomethingCritical()
.then((result) =>
doSomethingOptional(result)
.then((optionalResult) => doSomethingExtra(optionalResult))
.catch((e) => {}), // 第 5 行
)
// 無論 doSomethingOptional 成功失敗都會進行
.then(() => moreCriticalStuff())
.catch((e) => console.error(`Critical failure: ${e.message}`));
這樣的結構有一些特性
catch
只會處理 doSomethingOptional
和 doSomethingExtra
的錯誤。並且有處理後錯誤自然就不會再跑到外面去。一般而言,最佳做法是盡量不要有巢狀,鏈接越平越好,但必要時也可以利用結構來做適當的程式碼控制。