淺析KOA(1)

原由

起步學node比較晚,因此沒看express,直接看了koa,可是多數組件都是經過callback回調來執行,沒法直接在koa中使用,因此瞭解了下koa的實現原理,以便包裝現有組件或方法,這裏記錄下來細節,以做備份,並給你們作個參考吧。html

koa依賴co,配合thunkify來執行,雖然co如今已經改爲基於promise實現了,但其實原理基本差很少,我這裏先分析下co3.x版本。node

( 這裏須要你對 generator 有必定了解 )es6

分析

核心是利用generator能夠掛起或next(res)來繼續執行方法的特性。express

這裏不考慮異常處理流程,結合適當僞碼,給出一個簡易的demo便於理解co的控制流程:json

常規異步嵌套代碼:api

fs.readFile('template.html', function (err, template) { // 讀取模板
  asyncFunc('data.json', function (err, data) { // 獲取數據
    rep.end(render(template, data)); // 裝配並返回
  });
});

(注:上面的代碼僅做示意,並不考慮效率因素。實際上若是模板和數據都是異步的,咱們應該同時發起請求,co推薦將異步操做組成數組或對象,它在內部會作平行處理以便優化速度)數組

咱們指望的同步寫法:promise

var tempalte = readFile('template.html');
var data = asyncFunc('data.json');
rep.end(render(template, data));

爲了實現這個功能,咱們利用generator的特性,先作一個工具方法迭代generator:app

function core(genfunc) {
  var g = genfunc();

  (function next(err, res) {
    res = g.next(res);
    if (!res.done) {
      res.value(next);
    }
  })();
}

上面這個方法接收一個generatorFunction做爲參數,而後實例化它生成迭代器g,next方法不停的反覆調用g,並將上一次的返回值做爲參數不停的傳遞下去(有點像promise鏈式調用)。這裏有個地方注意:生成器每次返回的值必須是一個方法,並接受fn回調參數,這樣next方法才能做爲參數不停的迭代下去(co支持多種數據類型,它自行包裝了這些數據類型,最終仍是返回方法)koa

那麼下面就差最後一步了,包裝node標準api中的異步方法,以便支持yield:

function thunkify(fn) {
  return function () {
    var args = Array.prototype.slice.call(arguments, 0);
    return function (next) {
      fn.apply(this, args.concat(next));
    }
  }
}
// 包裝原生API
var readFile = thunkify(fs.readFile);

如今當調用readFile('path')的時候返回的是一個方法了,也就是yield的返回值,經過在core方法中,咱們來調用這個方法,並將next做爲參數傳遞給它,因而整個流程就能夠以同步方式執行下去了,咱們將剛纔的同步代碼經過core進行包裝,最終獲得:

core(function*() {
  var tempalte = yield readFile('template.html');
  var data = yield asyncFunc('data.json');
  rep.end(render(template, data));
});

結論

這裏asyncFunc我用來表示任何異步方法,它應該與readFile相似,返回另外一個方法並接受callback回調。標準API一般能夠經過thunkify來直接wrap,但若是是本身實現的方法必需要遵照這個約定。

以上代碼依賴generator特性,須要安裝最新版的node,當前最新版本是0.11.15,經過--harmony參數啓動,推薦安裝iojs,支持es6多數語法,用來學習瞭解新語法不錯。

相關文章
相關標籤/搜索