function compose (middlewares) {
return function (next) {
var i = middlewears.length;
var next = function* () {}();
while (i--) {
next = middlewares[i].call(this, next);
}
return next;
}
}
在koa的源码中有这样的代码:
var fn = this.experimntal
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));
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();
}
koa中间件执行流程
koa中间件的的执行顺序是洋葱模型,外层逐步向内,执行到最中间再逐步向外扩展,实现这个顺序的模型需要依赖于generator函数,它可以暂停执行将控制权交出,等到执行next再得到执行权继续执行,我们需要做的就是将generator串联起来,将后面的generator函数跟在上一层函数的yield语句之后,可以看作后面的函数是next的参数,这样我们就形成了一个串联,它的执行顺序就是我们前面所提到的洋葱模型。
koa-compose
在koa中,实现上面所说的串联函数就是利用了compose,下面是compose的大概实现(在koa中叫koa-compose):
在koa的源码中有这样的代码:
我们添加中间件的时候使用app.use方法,其实这个方法只是把中间件push到一个数组,很明显,所有的中间件在数组中,那么它们之间是没有联系的,所以我们会看到上面的代码,将所有的中间件都传入了我们所说的compose中。经过compose转换的代码是下面这样
co模块
上面我们看到通过使用koa-compose将中间件联系在一起(串联),可是在koa中需要调用next()方法才可以驱动函数向下执行。这时候就需要用到co模块。它可以帮我们自动管理generator的next,并根据调用返回value做出不同的响应;如果遇到另外一个generator,co会继续调用自己,这就是我们为什么需要co。 简单实现原理:
通过递归的方式(判断是否执行结束),来驱动generator的执行。
关于co模块的补充(es6)
ps:理解有限,如果有误请指出!