若為 Fulfilled Promise,我們可用 then()
或 await
去獲得 Synchronous 資料;但若為 Rejected Promise,則有 then()
、catch()
或 try catch
三種處理方式。
Version
macOS Mojave 10.14.6
VS Code 1.38.1
Quokka 1.0.243
ECMAScript 2017
Promise.resolve()
then()
let fetchData = () => Promise.resolve('Hello World');
fetchData().then(x => console.log(x));
若使用 Promise.resolve()
回傳 fulfilled promise,我們可用 then()
取得內部資料。
await
let fetchData = () => Promise.resolve('Hello World');
let x = await fetchData();
console.log(x);
亦可使用 ES2017 的 await
處理 fulfilled promise。
Promise.reject()
then()
let fetchData = () => Promise.reject('Something wrong');
fetchData().then(x => console.log(x));
若使用 Promise.reject()
回傳 rejected promise,則 then()
的 callback 將不會被執行。
紅色的 Something wrong
並非 console.log()
顯示,只是 Quokka 貼心地顯示 rejected promise 未處理,若在 browser 或 Node 則是 runtime 錯誤。
let fetchData = () => Promise.reject('Something wrong');
fetchData().then(
x => console.log(x),
e => console.log(e)
);
若要使用 then()
,正確寫法要提供第二個 callback,專門負責處理 rejected promise。
藍色的 Something wrong
才是 console.log()
所顯示。
catch()
let fetchData = () => Promise.reject('Something wrong');
fetchData()
.then(x => console.log(x))
.catch(e => console.log(e));
ES6 另外提供了 catch()
可專門處理 rejected promise。
如此 then()
只要專心處理 fulfilled promise 即可,更符合 SRP,可讀性更高。
.then(undefined, callback)
相當於.catch(callback)
Try Catch
let fetchData = () => Promise.reject('Something wrong');
try {
let x = await fetchData();
console.log(x);
}
catch (e) {
console.log(e);
}
ES2017 的 await
可搭配傳統的 try catch
,如此處理 fulfilled promise 寫在 try
block,而處理 rejected promise 寫在 catch
block,這種寫法與傳統 imperative 與 synchronous 相近。
try catch
只是.catch()
的 syntatic sugar 而已
Rejected in Fulfilled
then()
let fetchData = () => Promise.resolve('Hello World');
fetchData().then(
x => {
console.log(x);
return Promise.reject('Something wrong');
}
).then(x => console.log(x));
比較複雜的是若在 then()
的 callback 中回傳 rejected promise 時,若下一個 promise chain 的 then()
只提供一個 callback,一樣無法處理剛產生的 rejected promise。
Wrong again
無法被 console.log()
顯示,紅色的 Something worng
是 Quokka 所顯示。
let fetchData = () => Promise.resolve('Hello World');
fetchData().then(
x => {
console.log(x);
return Promise.reject('Something wrong');
},
e => console.log(e),
).then(
x => console.log(x),
e => console.log(e)
);
若要正確顯示 Something worng
,則第二個 then()
也要提供第二個 callback。
同理若要擔心第一個 rejected promise,第一個 then()
也必須提供第二個 callback。
可發現若要完整處理 rejected promise,則每個
then()
都要提供 rejected handler,如此就違反 DRY
catch()
let fetchData = () => Promise.resolve('Hello World');
fetchData().then(
x => {
console.log(x);
return Promise.reject('Something wrong');
}
).then(x => console.log(x)
).catch(e => console.log(e));
若改用 catch()
,則 rejected handler 只要寫一次即可,就可處理所有 rejected promise,也符合 functional 風格。
Try Catch
let fetchData = () => Promise.resolve('Hello World');
try {
let x = await fetchData();
console.log(x);
throw('Something wrong')
}
catch(e) {
console.log(e);
}
若使用 ES2017 的 await
,則 rejected handler 一樣只要寫一次即可,也符合原本 imperative 與 synchronous 習慣。
若要在 try
block 產生 rejected promise,直接使用 throw()
,這也是 syntatic sugar。
Conclusion
then()
必須每個 promise 都提供 rejected handler 才能完整處理 rejected promise,違反 DRY- 若使用
catch()
只需寫一個 rejected handler 即可,也符合 funtional 的 pipeline 習慣 try catch
也只需寫一個 rejected handler 即可,且可繼續使用 imperative 思維
Reference
Marius Schulz, Catch Errors in a JavaScript Promise Chain with Promise.prototype.catch()
MDN, Promise.resolve()
MDN, Promise.reject()
MDN, Promise.prototype.then()
MDN, Promise.prototype.catch()
MDN, await