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