Koa源碼分析(一) -- generator

Abstract

本系列是關於Koa框架的文章,目前關注版本是Koa v1。主要分爲如下幾個方面:python

  1. Koa源碼分析(一) -- generatores6

  2. Koa源碼分析(二) -- co的實現編程

  3. Koa源碼分析(三) -- middleware機制的實現segmentfault

Genetator函數

Generator函數是ES6提供的一種異步編程解決方案,其語法行爲徹底不一樣於傳統函數。框架

詳細解析可見阮一峯老師的《ECMAScript 6 入門 --- Generator》異步

語法

兩大特徵異步編程

  1. function 關鍵字與函數名之間的 *函數

  2. 函數體內部使用 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 對象,這等同於斷點就進入 subGeneratorsubGen裏面,等待 subGen 所有執行完後再回來繼續執行,相似於遞歸。
直白點說,就是將 subGen 內嵌到 mainGen中。

subGen函數中的return語句並不會起到斷點的做用

結束語

Generator函數做爲ES6的新特性,經過它能夠很好的解決JavaScript中的惡魔回調問題。Koa框架使用這特性很好組織了異步代碼的結構。

相關文章
相關標籤/搜索