KOA中間件源碼解析

告別回調噩夢,從這裏開始
express

KOA中間件的執行順序

請看下面來自官網的代碼和執行順序:koa

image

以上代碼的實現若是用回調函數來實現,無疑是一場噩夢,而KOA卻以十分優雅的方式實現了以下圖洋蔥圖通常的回調:函數

image

實現原理

核心是利用ES6的新特性:generatorcode

具體實現是利用KOA的兩個NIUBI轟轟的模塊:compose和CO中間件

compose

compose模塊,用於將全部generator中間件串聯起來,基本上就是將後一個generator賦給前一個generator的next參數。也就是在yield後面調用下一個generate函數。
大體原理:對象

// 中間件 a
function* a(next) {
  yield 1;

  // 執行下一個中間件
  yield* next;

  yield '繼續執行A中間件';
}

// 中間件 b
function* b(next) {
  yield 2;
  yield 3;
}


var next = function* (){};
var i = [a, b].length;

// 經過next首尾相連
while(i--) {
  next = [a, b][i].call(null, next);
}

// 包裹第一個middleware
function* start(ne) {
  return yield* ne;
}


// 輸出
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());
console.log(start(next).next());
輸出結果:

➜  a-lab ./a
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: '繼續執行A中間件', done: false }

這裏能夠看出來:compose取中間件是作i-循環的,可是因爲一開始是吧中間件推入棧中,順序爲FILO,全部順序是沒問題的。blog

可是:那個負責把全部中間件串起來的next其實自己也是一個generator,可是,若是在Generater函數內部,調用另外一個Generator函數,默認狀況下是沒有效果的。遞歸

因此這時候就輪到咱們的CO模塊出場啦路由

CO

CO模塊:CO模塊便經過遞歸使得嵌套好的generate依次自動執行(包裝爲Promise對象)
大體源碼實現generator

function run(gen){
  var g;
  if (typeof gen.next === 'function') {
    g = gen;
  } else {
    g = gen();
  }
  function next(){
    var tmp = g.next();
    //若是tmp.done爲true,那麼證實generator執行結束,返回。if (tmp.done) {
      return;
    } elseif (typeof g.next === 'function') {
      run(tmp.value);
      next();
    }
  }
  next();
}
  • co幫咱們"自動管理"generator的next,並根據調用返回的value作出不一樣的響應,這個響應是經過toPromise方法進行的
  • 若是遇到另一個generator,co會繼續調用本身,這就是爲何咱們不須要寫yield* next(使得next自執行)的緣由,而只要寫yield next
  • 並且CO模塊會判斷執行完全部的中間件,纔會對res進行操做,因此在中間件中咱們能夠隨意修改res,不會出現express中的路由已經終止請求-響應循環。
相關文章
相關標籤/搜索