Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

koa-中间件流程控制 #17

Open
cqupt-yifanwu opened this issue Apr 9, 2017 · 1 comment
Open

koa-中间件流程控制 #17

cqupt-yifanwu opened this issue Apr 9, 2017 · 1 comment

Comments

@cqupt-yifanwu
Copy link
Owner

koa中间件执行流程

koa中间件的的执行顺序是洋葱模型,外层逐步向内,执行到最中间再逐步向外扩展,实现这个顺序的模型需要依赖于generator函数,它可以暂停执行将控制权交出,等到执行next再得到执行权继续执行,我们需要做的就是将generator串联起来,将后面的generator函数跟在上一层函数的yield语句之后,可以看作后面的函数是next的参数,这样我们就形成了一个串联,它的执行顺序就是我们前面所提到的洋葱模型。

koa-compose

在koa中,实现上面所说的串联函数就是利用了compose,下面是compose的大概实现(在koa中叫koa-compose):

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));

我们添加中间件的时候使用app.use方法,其实这个方法只是把中间件push到一个数组,很明显,所有的中间件在数组中,那么它们之间是没有联系的,所以我们会看到上面的代码,将所有的中间件都传入了我们所说的compose中。经过compose转换的代码是下面这样

//达到了洋葱模型的效果
function *() {
    yield *g1(g2(g3()))
}

co模块

上面我们看到通过使用koa-compose将中间件联系在一起(串联),可是在koa中需要调用next()方法才可以驱动函数向下执行。这时候就需要用到co模块。它可以帮我们自动管理generator的next,并根据调用返回value做出不同的响应;如果遇到另外一个generator,co会继续调用自己,这就是我们为什么需要co。
简单实现原理:

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的执行。

关于co模块的补充(es6)
  • co会返回一个Promise对象,因此我们可以使用then方法添加回调函数
  • co真正的源码做了什么
    • 检查当前代码是否为Generator函数的最后一步,如果是就返回
    • 确保每一步返回的结果都是promise对象
    • 使用then方法为返回值加上回调函数,然后通过onFulfilled函数再次调用next函数
    • 在参数不符合要求的情况下将promise状态改为Rejected从而终止模块。

ps:理解有限,如果有误请指出!

@cqupt-yifanwu
Copy link
Owner Author

koa-compose 中 next = middlewares[i].call(this, next); 不太好理解:首先这是个递减的过程,我们会先取到最后的一个函数 然后,next函数其实是起到一个中介的作用,将next传入后又重新更新了next也就是在下一此的运行中next函数是带有刚刚那个最内层的函数的(最后一个)于是再进行操作,是一个递归传入的过程,可以看 这篇文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant