ECMAScript 2015 的最大亮點之一就是提出 Promise 這種 未來值 概念避免 Callback Hell,先有 2015 的 then(),後有 2017 的 await,都可用來取得 Promise 內的 Synchronous 資料。
Version
macOS Mojave 10.14.6
VS Code 1.38.1
Quokka 1.0.243
ECMAScript 2017
Promise.resolve()
let fetchData = () => Promise.resolve({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
fetchData(); // ?
若 data 是 synchronous,可直接回傳沒問題,但若 data 是 asynchronous,則必須透過 Promise.resolve() 將 data 包成 promise 後回傳。
若直接讀取 fetchData(),會發現顯示多顯示了 then,表示其資料為 promise。

let fetchData = async() => ({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
fetchData(); // ?
也可使用 ES2017 的 async 修飾 arrow function,表示回傳 promise,相當於 Promise.resolve() 的 syntatic sugar。

then()
let fetchData = async() => ({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
fetchData().then(x => console.log(x));
該如何取得不包含 then 的資料呢 ? 要使用 promise 自帶的 then(),由其 callback 的 x 取得內部 synchronous 資料。

如此資料就不再包含 then 了。
let fetchData = async() => ({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
fetchData().then(x => console.log(x.data));
若要取得 data property 下的資料呢 ? 直覺會使用 x.data 取得。

但其實 x => console.log(x.data) 包含了兩件事情:
- 從
x.data取得資料 - 使用
console.log()印出
基於單一職責原則 (SRP),我們會希望 callback 只做一件事情,因次比較好的方式是使用兩次 then()。
let fetchData = async() => ({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
fetchData()
.then(x => x.data)
.then(x => console.log(x));
第 10 行
.then(x => x.data)
回傳仍是 promise,只是其內部資料改成 x.data,相當於完成從 x.data 取得資料。
.then(x => console.log(x));
由於之前回傳是 promise,因此可繼續使用 then() 抓到 x.data,最後使用 console.log() 印出。

Await
let fetchData = async() => ({
data: [
{ title: 'FP in JavaScript', price: 200 },
{ title: 'RxJS in Action', price: 100 },
{ title: 'Speaking JavaScript', price: 300 }
]
});
let x = await fetchData();
console.dir(x.data);
也可使用 ES2017 的 await 取得 promise 內的 synchronous 資料,這種寫法與傳統 imperative 與 synchronous 相近,只多了 await 修飾而已。

Conclusion
- 使用 Promise 的
then()時,應謹記每個 callback 只做一件事情,且為 pure function 不應該有 side effect - Side effect 實務上無法避免,但不應在
then()的 callback 處理,而是交給 function 的呼叫者處理 - Await 則無 side effect 概念,可繼續使用 imperative 思維
Reference
Marius Schulz, Create a Promise Chain in JavaScript with Promise.prototype.then()
MDM, Promise.resolve()
MDM, async function
MDN, Promise.prototype.then()
MDN, await