Async Function = A, Generator = G, A ≠ G, ∃A ∈ G, ∃G ∈ A
Generator 我就不再介紹了,大家的用法看 Koa/co,真正意義看 https://zhuanlan.zhihu.com/p/20794401。
我們來說說 async/await,自從 TJ 創造了 co 以後,一眾 JavaScript Diaosiloper 就瘋狂起來了:WOW,可以直接寫同步代碼了!
後來 async/await 進入 ECMAScript 的 Draft,是的,到今天為止還沒有成為標準。就有人覺得,這不就是 co 的東西嗎?嗯,可以說 co 就是模仿 async/await 來創造的。這裡又要說到什麼是“協程”了,對不起,JavaScript 暫時並沒有資格說這個,所以我以 Go 語言來講講吧。
package main
import "fmt"
func Add(x, y int, ch chan int) {
z := x + y
ch <- z
}
func main() {
chs := make([]chan int, 10)
for i := 0; i < 10; i++ {
chs[i] := make(chan int)
go Add(i, i + 1, chs[i])
}
for _, ch := range(chs) {
val := <- ch // Waiting each coroutine to finish
fmt.Println(val)
}
}
Go 語言中利用 go
語句來將一個函數(Add
)作為協程的運行內容創建一個協程,并通過 channel 特性來作為主線程與協程之間的通訊通道,因為 channel 的寫入和讀取具有原子性且同步的,所以如果協程內對 channel 的寫入並沒有完成,主線程中的讀取就會一直等待。
這樣的好處是,可以將複雜的、耗時長的、甚至堵塞的代碼放在協程中運行,而主線程中只要在需要的時候把結果取出來便可以了。這就是協程的意義,OK。
迴歸正題,async/await 跟 Generator 究竟有什麼差別?首先你要明白一點,兩者都可以完成類似於協程的任務。而如果非要說之間的差別,我可以這樣總結:
Async Function is a common function, Generator Function is not.
這話題一開始我就說了:
Async Function = A, Generator = G, A ≠ G, ∃A ∈ G, ∃G ∈ A
那麼在什麼情況下,他們之間存在包含關係呢?
Async Function ∈ (Generator + co)
是的,當 Generator 結合 co 時,Async Function 就變成了他們的子集,這就是 co 厲害的地方。
當然,這是在 Node.js 中的情況。那麼在前端開發中,情況又會是怎麼樣的呢?
我們知道前端開發需要響應用戶的操作事件,最基本就是點擊。假設有這樣一個場景,用戶在 input
中輸入一個 key,然後響應函數中需要從 MinDB 中讀取相應 key 的數據,并顯示在頁面上。
<div id="app">
<input type="text" v-model="key">
<p>{{value}}</p>
<button @click="query">Query</button>
</div>
<script>
import Vue from 'vue'
import min from 'min'
new Vue({
el: '#app',
data: {
key: '',
value: ''
},
methods: {
query() {
min.get(this.key)
.then(value => this.value)
}
}
})
</script>
我們可以看到 MinDB 的數據讀取是異步的,而且使用的是 Promise,那麼自然就想到了可不可以通過技術手段來變成“同步代碼”呢?
我們直接來看 async/await 吧。
methods: {
async query() {
this.value = await min.get(this.key)
}
}
好的,完事了。如果用 Generator 呢?
methods: {
query: function* () {
this.value = yield min.get(this.key)
}
}
嗯,看上去也不錯,但是運行之後便發現,沒報錯、也沒結果。為什麼?因為 Generator 並沒有運行起來。用 co 吧?
methods: {
query() {
const self = this
co(function* () {
self.value = yield min.get(self.key)
})
}
}
ok,其餘自己想吧。