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