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中的路由已经终止请求-响应循环。
相关文章
相关标签/搜索