本系列是關於Koa框架的文章,目前關注版本是Koa v1。主要分爲如下幾個方面:python
Koa源碼分析(三) -- middleware機制的實現segmentfault
Generator函數是ES6提供的一種異步編程解決方案,其語法行爲徹底不一樣於傳統函數。框架
詳細解析可見阮一峯老師的《ECMAScript 6 入門 --- Generator》異步
兩大特徵異步編程
function
關鍵字與函數名之間的 *
函數
函數體內部使用 yield
語句源碼分析
咱們定義一個generatorFunction示例:code
function* firstGenerator() { var one = yield 'one'; console.log(one); var two = yield 'two'; concole.log(two); var third = yield 'third'; console.log(third); return 'over' }
帶有 *
的函數聲明就表明 firstGenerator
函數是一個generator函數,函數裏面的 yield 關鍵字能夠理解爲在當前位置設置斷點,這一點若有疑問,能夠看後續。
那generator函數的語法行爲究竟與傳統函數不一樣在哪裏呢?下邊咱們來梳理下generator函數的運行步驟。
var it = firstGenerator(); console.log(it.next(1)); // {value: "one", done: false} console.log(it.next(2)); // {value: "two", done: false} console.log(it.next(3)); // {value: "third", done: false} console.log(it.next(4)); // {value: "over", done: true}
首先經過執行 firstGenerator
函數,咱們能夠獲得一個generator對象 it
,它是一個迭代器對象。此時, firstGenerator
函數並未執行,只是返回了迭代器對象 it
,咱們能夠經過 it
對象中 next
方法觸發 firstGenerator
函數開始執行,此時咱們調用 it.next(1)
,注意 next
注入的參數 1
並無任何效果。當 firstGenerator
函數執行到 yield
語句時,被打了斷點,停留在此處,並將 yield
後的變量傳遞給 it.next(1)
結果對象中的 value
字段,另外其中的 done
字段表示 firstGenerator
函數還沒有徹底執行完,還停留在斷點。以此同時,將執行權交換給 it.next(1)
。
執行第二次 it.next(2)
,執行權再次交給 firstGenerator
函數,並將 next
帶入的參數傳遞給函數中的變量 one
,此時輸出 2
。當運行到 yield 'two'
語句時,再次將執行權返回給 it.next(2)
並傳值。
第三次執行 it.next(3)
的過程與第二次徹底同樣。
最後一次執行 it.next(4)
時,在此以前, firstGenerator
函數斷點在 var third = yield 'third'
,當 it.next(4)
將執行權交給 firstGenerator
函數時,將 4
傳遞給變量 third
,此刻輸出 4
。當執行到 return
語句時,整個函數已經執行完了,並將 'over'
傳遞給 it.next(4)
返回結果中的 value
字段,且 done
字段爲 true
。
若沒有 return
語句,則 value
字段返回 null
。
這樣下來,整個 firstGenerator
整個函數執行完畢。
咱們能夠將Generator函數比喻成懶惰的癩蛤蟆,每次都須要使用it.next()方法戳一下,纔會有對應的行動。
若是你們瞭解python中協程的概念,應該很好理解Generator函數的語法行爲。
在Generator函數中 yield
的語法,其後邊的值也能夠是函數、對象等等。 yield
後邊能夠是另外一個Generator對象。
function* subGen() { console.log('step in sub generator'); var b = yield 'sub 1'; console.log(b); console.log('step out sub generator'); } var subGenerator = new subGen(); function* mainGen() { var a = yield 'main 1'; console.log(a); var b = yield *subGenerator; console.log(b); var c = yield 'main 2'; console.log(c); return 'over'; } var it = mainGen(); console.log(it.next(1)); // {value: 'main 1', done: false} console.log(it.next(2)); // 2 // step in sub generator // {value: 'sub 1', done: false} console.log(it.next(3)); // 3 // step out sub generator // null // {value: 'main 2', done: false} console.log(it.next(4)); // 4 // {value: 'over', done: true}
yield
後面跟着 *subGenerator
對象,這等同於斷點就進入 subGenerator
的 subGen
裏面,等待 subGen
所有執行完後再回來繼續執行,相似於遞歸。
直白點說,就是將 subGen
內嵌到 mainGen
中。
在subGen
函數中的return語句並不會起到斷點的做用
Generator函數做爲ES6的新特性,經過它能夠很好的解決JavaScript中的惡魔回調問題。Koa框架使用這特性很好組織了異步代碼的結構。