若為 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