ECMAScript 2015 提供了 catch()
處理 Rejected Promise,而 2017 更提供了 try catch
直接在 catch
Block 處理;事實上 Ramda 也提供了 otherwise()
,讓我們以 Function Pipeline 處理 Rejected Promise。
Version
Ramda 0.27.1
Fulfilled Promise
then()
let f = () => Promise.resolve(1)
f().then(console.log)
若使用 Promise.resolve()
回傳 Fulfilled Promise,可用 then()
取得內部資料。
Await
let f = () => Promise.resolve(1)
await f() // ?
亦可使用 ES2017 的 await
處理 Fulfilled Promise。
Rejected Promise
then()
let f = () => Promise.reject('error')
f().then(console.log)
若使用 Promise.reject()
回傳 Rejected Promise,則 then()
的 callback 將不會被執行。
紅色的 error
並非 console.log()
顯示,只是 Quokka 貼心地顯示 Rejected Promise 未處理,若在 browser 或 Node 則是 runtime 錯誤。
let f = () => Promise.reject('error')
f().then(console.log, console.error)
若要使用 then()
,正確寫法要提供第二個 callback,專門負責處理 Rejected Promise。
紅色的 error
才是 console.error()
所顯示。
catch()
let f = () => Promise.reject('error')
f()
.then(console.log)
.catch(console.error)
ES6 另外提供了 catch()
可專門處理 Rejected Promise。
如此 then()
只要專心處理 Fulfilled Promise 即可,更符合 SRP且可讀性更高。
.then(undefined, callback)
相當於.catch(callback)
Try Catch
let f = () => Promise.reject('error')
try {
let x = await f()
console.log(x)
}
catch (e) {
console.error(e)
}
ES2017 的 await
可搭配傳統的 try catch
,如此處理 Fulfilled Promise 寫在 try
block,而處理 Rejected Promise 寫在 catch
block,與傳統 Imperative 與 synchronous 相近。
try catch
只是.catch()
的 syntatic sugar 而已
Ramda
import { pipe, andThen as then, otherwise } from 'ramda'
let f = () => Promise.reject('error')
pipe(
f,
then(console.log),
otherwise(console.error)
)()
Ramda 已經提供 then()
讓我們處理 Fulfilled Promise,同理也提供了 otherwise()
處理 Rejected Promise。
otherwise()
(a → b) → (Promise e a) → (Promise e b)
Promise.prototype.catch()
的 function 版本
(a -> b)
:synchronous function
Promise e a
:data 為 Promise
Promise e b
:回傳亦為 Promise
Rejected in Fulfilled
then()
let f = () => Promise.resolve(1)
f()
.then(x => (console.log(x), Promise.reject('error')))
.then(console.log)
比較複雜的是若在 then()
的 callback 中回傳 Rejected Promise 時,若下一個 then()
只提供一個 callback,一樣無法處理剛產生的 rejected promise。
error
無法被 console.log()
顯示,紅色的 Something worng
是 Quokka 所顯示。
let f = () => Promise.resolve(1)
f()
.then(x => (console.log(x), Promise.reject('error')), console.error,)
.then(console.log, console.error)
若要正確顯示 error
,則第二個 then()
也要提供第二個 callback。
同理若要擔心第一個 Rejected Promise,第一個 then()
也必須提供第二個 callback。
可發現若要完整處理 Rejected Promise,則每個
then()
都要提供 rejected handler 很麻煩
catch()
let f = () => Promise.resolve(1)
f()
.then(x => (console.log(x), Promise.reject('error')))
.then(console.log)
.catch(console.error)
若改用 catch()
,則 Rejected Handler 只要寫一次即可,就可處理所有 Rejected Promise。
Try Catch
let f = () => Promise.resolve(1)
try {
let x = await f()
console.log(x)
throw('error')
}
catch(e) {
console.log(e)
}
若使用 ES2017 的 await
,則 Rejected Handler 一樣只要寫一次即可,也符合原本 Imperative 與 synchronous 習慣。
Ramda
import { pipe, andThen as then, otherwise } from 'ramda'
let f = () => Promise.resolve(1)
pipe(
f,
then(x => (console.log(x), Promise.reject('error'))),
otherwise(console.error)
)()
若在 then()
回傳 Rejected Promise,則 otherwise()
也能攔截得到 error
,與 catch()
及 try catch
一樣只需一個 Rejected Handler 即可。
Conclusion
then()
必須每個 Promise 都提供 Rejected Handler 才能完整處理 rejected promise,若使用catch()
只需寫一個 Rejected Handler 即可try catch
也只需寫一個 Rejected Handler 即可,且可繼續使用 Imperative 思維otherwise()
也只需寫一個 Rejected Handler 即可,由於其 functional 特質,可繼續使用 Function Pipeline
Reference
Marius Schulz, Catch Errors in a JavaScript Promise Chain with Promise.prototype.catch()
Ramda, otherwise()