koa中間件的的執行順序是洋蔥模型,外層逐步向內,執行到最中間再逐步向外擴展,實現這個順序的模型須要依賴於generator函數,它能夠暫停執行將控制權交出,等到執行next再獲得執行權繼續執行,咱們須要作的就是將generator串聯起來,將後面的generator函數跟在上一層函數的yield語句以後,能夠看做後面的函數是next的參數,這樣咱們就造成了一個串聯,它的執行順序就是咱們前面所提到的洋蔥模型。node
在koa中,實現上面所說的串聯函數就是利用了compose,下面是compose的大概實現(在koa中叫koa-compose):es6
function compose (middlewares) { return function (next) { var i = middlewears.length; var next = function* () {}(); while (i--) { next = middlewares[i].call(this, next); <!--這一點不太好理解:首先這是個遞減的過程,咱們會先取到最後的一個函數 而後,next函數實際上是起到一箇中介的做用,將next傳入後又從新更新了next 也就是在下一此的運行中next函數是帶有剛剛那個最內層的函數的(最後一個) 因而再進行操做,是一個遞歸傳入的過程,能夠看這篇文章:https://cnodejs.org/topic/5723360e35af8a704195f5d5 --> } return next; } }
在koa的源碼中有這樣的代碼:數組
var fn = this.experimntal ? compose_es7(this.middleware) : co.wrap(compose(this.middleware));
咱們添加中間件的時候使用app.use方法,其實這個方法只是把中間件push到一個數組,很明顯,全部的中間件在數組中,那麼它們之間是沒有聯繫的,因此咱們會看到上面的代碼,將全部的中間件都傳入了咱們所說的compose中。通過compose轉換的代碼是下面這樣promise
//達到了洋蔥模型的效果 function *() { yield *g1(g2(g3())) }
上面咱們看到經過使用koa-compose將中間件聯繫在一塊兒(串聯),但是在koa中須要調用next()方法才能夠驅動函數向下執行。這時候就須要用到co模塊。它能夠幫咱們自動管理generator的next,並根據調用返回value作出不一樣的響應;若是遇到另一個generator,co會繼續調用本身,這就是咱們爲何須要co。
簡單實現原理:app
function run (gen) { var g; if (typeof gen.next === 'function') { g = gen; } else { g = gen(); } function next () { var tmp = g.next(); if (tmp.done) { return; } else if (typeof g.next === 'function') { run(tmp.value); // 將下一步傳入run函數當中 next(); } } next(); }
經過遞歸的方式(判斷是否執行結束),來驅動generator的執行。koa