最近需要单独使用koa-compose模块。虽然在使用koa的时候大致知道了中间件的执行过程,但是在没有仔细研究源码的情况下还是不放心(主要是这个模块代码少,如果多了,我没兴趣研究它)。koa-compose似乎代码较少,但它确实存在。闭包、递归、Promises。..又看了一遍,想不通。看了网上几篇解读文章,都是对单行代码的解释,但还是看不懂。最后只好采用了傻逼的方式:koa-compose去掉一些注释,类型校验后源码如下:functioncompose(middleware){returnfunction(context,next){//lastcalledmiddleware#letindex=-1returndispatch(0)functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()被多次调用'))index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.解决()尝试{返回承诺。resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}}}编写如下代码:varindex=-1;functioncompose(){returndispatch(0)}functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()被多次调用'))index=ivarfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve('fnisundefined')try{returnPromise.解决(fn(上下文,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}functionf1(context,next){console.log('middleware1');next().then(data=>console.log(data));console.log('中间件1');返回“中间件1返回”;}functionf2(context,next){console.log('middleware2');next().then(data=>console.log(data));console.log('中间件2');返回“中间件2返回”;}functionf3(context,next){console.log('middleware3');next().then(data=>console.log(data));console.log('中间件3');返回'中间件3返回';}varmiddleware=[f1,f2,f3]varcontext={};varnext=function(context,next){console.log('middleware4');next().then(data=>console.log(data));console.log('中间件4');return'middleware4return';};compose().then(data=>console.log(data));直接运行结果如下:"middleware1""middleware2""middleware3""middleware4""middleware4""中间件3""中间件分别是2""中间件1""fn未定义""中间件4返回""中间件3返回""中间件2返回""中间件1返回"按照代码运行过程一步步分析:dispatch(0)i==0,index==-1i>indexdownindex=0fn=f1Promise.resolve(f1(context,dispatch.bind(null,0+1)))这会执行f1(context,dispatch.bind(null,0+1))进入f1执行上下文console.log('middleware1');输出中间件1next()其实就是调用dispatch(1)bind并递归启动dispatch(1)i==1,index==0的功劳i>indexdownindex=1fn=f2Promise.resolve(f2(context,dispatch.bind(null,1+1)))这样会执行f2(context,dispatch.bind(null,1+1))进入f2执行console.log('middleware2');输出中间件2next()其实是调用dispatch(2)然后递归dispatch(2)i==2,index==1i>indexdownindex=2fn=f3Promise.resolve(f3(context,dispatch.bind(null,2+1)))这会执行f3(context,dispatch.bind(null,2+1))进入f3执行上下文console.log('middleware3');输出中间件3next()其实调用了dispatch(3)然后递归dispatch(3)i==3,index==2i>index往下走index=3i===middleware.lengthfn=nextPromise.resolve(next(context,dispatch.bind(空,3+1)))这样会执行next(context,dispatch.bind(null,3+1))进入下一个执行上下文console.log('middleware4');输出中间件4next()其实就是调用dispatch(4)然后递归dispatch(4)i==4,index==3i>indexgodownindex=4fn=middleware[4]fn=undefinedreuturnPromise.resolve('fnisundefined')返回下一个执行上下文console.log('middleware4');outputmiddleware4return'middleware4return'Promise.resolve('middleware4return')returnstof3executioncontextconsole.log('middleware3');outputmiddleware3return'middleware3return'Promise.resolve('middleware3return')返回到f2执行上下文console.log('middleware2');outputmiddleware2return'middleware2return'Promise.resolve('middleware2return')返回到f1执行上下文控制台。日志('中间件1');outputmiddleware1return'middleware1return'Promise.resolve('middleware1return')返回全局上下文到目前为止已经输出“middleware1”“middleware2”“middleware3”“middleware4”“middleware4”“middleware3""中间件2""中间件1"那"fn未定义""中间件4返回""中间件3返回""中间件2返回""中间件1返回"怎么回来的看一下,每个中间件里面都有next().then(data=>console.log(data));根据前面的分析,then中第一个结果应该是下一个中间件,结果就是Promise.resolve('fnisundefined')的结果,然后分别是f4,f3,f2,f1那为什么最后输出呢?Promise.resolve('fn未定义').then(data=>console.log(data));console.log('中间件4');运行或setTimeout(()=>console.log('fnisundefined'),0);后就清楚了console.log('中间件4');整个调用过程也可以看成这样:,function(){returnPromise.resolve(next(context,function(){returnPromise.resolve('fnisundefined')}))}))}))}))}composeDetail().then(data=>console.log(数据));方法虽然笨,但是compose的作用不言而喻。最后,if(i<=index)returnPromise.reject(newError('next()calledmultipletimes'))这段代码什么时候恢复生效呢?在一个中间件中调用两次next(),按照上面的套路,相信很快你就会明白了。
