起步學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多數語法,用來學習瞭解新語法不錯。