ECAMAcript 2015 提供了 Promise 與 then
,而 ECMAScript 2017 更支援了 async await
,這兩種寫法可彼此交換,但常有人只看得懂其中一種寫法,維護其他 Codebase 就看不懂了,本文特別說明之。
Version
Knex 0.20.9
Promise vs. Async Await
let books = (_, { price }) => mySQL ('books')
.select ('*')
.where ({ price })
books
沒宣告為 async
function。
let books = async (_, { price }) => {
let result = await mySQL ('books')
.select ('*')
.where ({ price })
return result[0]
}
但 books
卻宣告為 async
function。
同樣是 Apollo GraphQL 搭配 Knex,為什麼有兩種完全不同寫法呢 ?
Knex
首先釐清一個觀念,Knex 回傳為 Promise,且 books
query 回傳也是 Promise。
let books = (_, { price }) => mySQL ('books')
.select ('*')
.where ({ price })
Knex 回傳為 Promise,且沒經過任何處理,一路就是只有回傳 Promise 而已。
let books = async (_, { price }) => {
let result = await mySQL ('books')
.select ('*')
.where ({ price })
return result[0]
}
但經過 await
處理之後又不太一樣。
let result = await mySQL ('books')
.select ('*')
.where ({ price })
result
透過 await
從 Promise 取出,如此 result
就不是 Promise 了,而是普通的 Array。
return result[0]
也因為 result
是 Array,因此可使用 []
方式取出第一筆 element。
let books = async (_, { price }) => {
但因為 books
必須回傳 Promise,所以最後又必須加上 async
包成 Promise。
也可發現只要使用
await
,就必須搭配async
,這是 ECMAScript 語法規定
await
只是暫時從 Promise 取出值,最後還是得重新包回 Promise
then
let books = async (_, { price }) => mySQL ('books')
.select ('*')
.where ({ price })
.then (x => x[0])
其實比較好寫法是直接提供 pure function 給 then
,讓我們直接去改 Promise 內部的資料,如此就不必透過 await
取出資料,再透過 async
重新包成 Promise。
Ramda
import { nth } from 'ramda'
let books = async (_, { price }) => mySQL ('books')
.select ('*')
.where ({ price })
.then (nth(0))
若你想讓 then
的 callback 也 Point-free,也可使用 Ramda 的 nth
。
Conclusion
- 別忘了 Promise 具有 functor 特性,且
then
也相當於map
,因此可直接提供 pure function 修改 Promise 內部資料