You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionco(gen){varctx=this;varargs=slice.call(arguments,1)// we wrap everything in a promise to avoid promise chaining,// which leads to memory leak errors.// see https://github.com/tj/co/issues/180returnnewPromise(function(resolve,reject){if(typeofgen==='function')gen=gen.apply(ctx,args);if(!gen||typeofgen.next!=='function')returnresolve(gen);onFulfilled();/** * @param {Mixed} res * @return {Promise} * @api private */functiononFulfilled(res){varret;try{ret=gen.next(res);}catch(e){returnreject(e);}next(ret);}/** * @param {Error} err * @return {Promise} * @api private */functiononRejected(err){varret;try{ret=gen.throw(err);}catch(e){returnreject(e);}next(ret);}/** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */functionnext(ret){if(ret.done)returnresolve(ret.value);varvalue=toPromise.call(ctx,ret.value);if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);returnonRejected(newTypeError('You may only yield a function, promise, generator, array, or object, '+'but the following object was passed: "'+String(ret.value)+'"'));}});}
functionnext(ret){if(ret.done)returnresolve(ret.value);varvalue=toPromise.call(ctx,ret.value);if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);returnonRejected(newTypeError('You may only yield a function, promise, generator, array, or object, '+'but the following object was passed: "'+String(ret.value)+'"'));}
前言
原文地址
用在前面
用过koa的同学都知道添加中间件的方式是使用koa实例的
use
方法,并传入一个generator函数,这个generator函数可以接受一个next
(这个next到底是啥?这里先不阐明,在后面会仔细说明)。执行use干了嘛
这是koa的构造函数,为了没有其他信息的干扰,我去除了一些暂时用不到的代码,这里我们把目光聚焦在
middleware
这个数组即可。接下来我们要看use方法了
同样去除了一些暂时不用的代码,可以看到每次执行use方法,就把外面传进来的generator函数push到middleware数组中
好啦!你已经知道koa中是预先通过use方法,将请求可能会经过的中间件装在了一个数组中。
接下来我们要开始本文的重点了,当一个请求到来的时候,是怎样经过中间件,怎么跑起来的
首先我们只要知道下面这段
callback
函数就是请求到来的时候执行的回调即可(同样尽量去除了我们不用的代码)这段代码可以分成两个部分
我们分部分来说一下
这段代码对
experimental
做了下判断,如果设置为了true
那么koa中将可以支持传入async函数,否则就执行co.wrap(compose(this.middleware))
。只有一行初始化中间件就做完啦?
我知道koa很屌,但也别这么屌好不好,所以说评价一个好的程序员不是由代码量决定的
我们来看下这段代码到底有什么神奇的地方
把装着中间件
middleware
的数组作为参数传进了compose
这个方法,那么compose做了什么事呢?其实就是把原本毫无关系的一个个中间件给首尾串起来了,于是他们之间就有了千丝万缕的联系。文字解释一下就是,compose将中间件从最后一个开始处理,并一直往前直到第一个中间件。其中非常关键的就是将后一个中间件得到generator对象作为参数(
这个参数就是文章开头说到的next啦,也就是说next其实是一个generator对象
)传给前一个中间件。当然最后一个中间件的参数next
是一个空的generator函数生成的对象。我们自己来写一个简单的例子说明compose是如何将多个generator函数串联起来的
看到了吗?中间件被串起来之后执行的顺序是
gen1 -> gen2 -> gen3 -> noop -> gen3 -> gen2 -> gen1
从而首尾相连,进而发生了关系😈。
co.wrap
所有上述代码可以理解为
好,我们再看看
co.wrap
做了什么,慢慢地一步步靠近了哦可以看到
co.wrap
返回了一个普通函数createPromise
,这个函数就是文章开头的fn
啦。中间件开始跑起来啦
这一段便是请求到来手即将要经过的中间件执行部分,fn执行之后返回的是一个Promise,koa通过注册成功和失败的回调函数来分别处理请求。
让我们回到
createPromise
里面的fn就是经过compose处理中间件后返回的一个generator函数,那么执行之后拿到的就是一个generator对象了,并把这个对象传经经典的co里面啦。如果你需要对co的源码了解欢迎查看昨天写的走一步再走一步,揭开co的神秘面纱,好了,接下来就是看co里面如何处理这个被compose处理过的generator对象了再回顾一下co
我们直接看一下
onFulfilled
,这个时候第一次进co的时候因为已经是generator对象所以会直接执行onFulfilled()
而
gen.next
正是用于去执行中间件的业务逻辑,当遇到yield语句的时候,将紧随其后的结果返回赋值给ret
,通常这里的ret,就是我们文中说道的next
,也就是当前中间件的下一个中间件。拿到下一个中间件后把他交给
next
去处理当中间件执行结束了,就把Promise的状态设置为成功。否则就将
ret
(也就是下一个中间件)再用co包一次。主要看toPromise
的这几行代码即可注意噢
toPromise
这个时候的返回值是一个Promise,这个非常关键,是下一个中间件执行完成之后回溯到上一个中间件中断执行处继续执行的关键看到这里,我们可以总结出,几乎koa的中间件都会被co给包装一次,而每一个中间件又可以通过Promise的then去监测其后一个中间件是否结束,后一个中间件结束后会执行前一个中间件用then监听的操作,这个操作便是执行该中间件yield next后面的那些代码
打个比方:
当koa中接收到一个请求的时候,请求将经过两个中间件,分别是
中间件1
和中间件2
,中间件1
中间件2
那么处理的过程就是co会立即调用onFulfilled来执行中间件1前半部分代码,遇到
yield 中间件2
的时候得到中间件2generator对象,紧接着,又把这个对象放到co里面继续执行一遍,以此类推下去知道最后一个中间件(我们这里的指的是那个空的noop中间件)执行结束,继而马上调用promise的resolve方法表示结束,ok,这个时候中间件2监听到noop执行结束了,马上又去执行了onFulfilled来执行yield noop中间件后半部分代码,好啦这个时候中间件2也执行结束了,也会马上调用promise的resolve方法表示结束,ok,这个时候中间件1监听到中间件2执行结束了,马上又去执行了onFulfilled来执行yield 中间件2后半部分代码,最后中间件全部执行完了,就执行respond.call(ctx);啊 啊 啊好绕,不过慢慢看,仔细想,还是可以想明白的。用代码表示这个过程有点类似
结尾
如果对你理解koa有些许帮助,不介意的话,点击源码地址点颗小星星吧
如果对你理解koa有些许帮助,不介意的话,点击源码地址点颗小星星吧
如果对你理解koa有些许帮助,不介意的话,点击源码地址点颗小星星吧
源码地址
The text was updated successfully, but these errors were encountered: