中间件特性|||中间件1||||+------------------------------------------------------------+|||||||中间件2||||||||+--------------------------------+|||||||||行动|行动|中间件3|行动||001|002||005|006||||行动行动||||||003004||||||||||+------------------------------------------------------------------------------------------------>|||||||||||||||+----------------------------------+|||+----------------------------------------------------------+|+------------------------------------------------------------------------------+先写一段贯穿全文的koa代码constkoa=require('koa');letapp=newKoa();constmiddleware1=async(ctx,next)=>{console.日志(1);等待下一个();控制台日志(6);}constmiddleware2=async(ctx,next)=>{console.log(2);等待下一个();控制台日志(5);}constmiddleware3=async(ctx,next)=>{console.log(3);等待下一个();控制台日志(4);}应用程序使用(中间件1);应用程序使用(中间件2);应用程序使用(中间件3);应用程序。use(async(ctx,next)=>{ctx.body='helloworld'})app.listen(3001)//output1,2,3,4,5,6awaitnext()使得每个中间件分成,预操作,等待其他中间件操作。可以观察中间件的特点:contextctxawaitnext()控制前后操作。后期操作类似数据解构-栈,先进后出的Promise模拟实现Promise.resolve(middleware1(context,async()=>{returnPromise.resolve(middleware2(context,async()=>{returnPromise.resolve(middleware3(context,async()=>{returnPromise.resolve();}));}));})).then(()=>{console.log('end');});从这段模拟代码可以知道next()返回的是一个promise,我们需要使用await来等待promise的解析valuepromises的嵌套就像洋葱模型的形状,一层一层地包裹起来直到返回最内层等待的promise的resolve值。思考:如果next()不加await,执行顺序是怎样的?在这个例子中,如果next()的执行顺序和awaitnext()是一样的,因为next前面的操作是同步的。如果前面的操作是异步的呢?constp=function(args){returnnewPromise(resolve=>{setTimeout(()=>{console.log(args);resolve();},100);});};constmiddleware1=async(ctx,next)=>{awaitp(1);//等待下一个();下一个();控制台日志(6);};constmiddleware2=async(ctx,next)=>{awaitp(2);//等待下一个();下一个();console.log(5);};constmiddleware3=async(ctx,next)=>{awaitp(3);//等待下一个();下一个();console.log(4);};//输出结果:1,6,2,5,3,4当程序执行到middleware1时,执行到awaitp(1)等待promise值返回并跳出and然后进入下一个事件循环,执行next()就是执行到middleware2,然后执行到awaitp(2)等待promise值返回,跳出middleware2,回到middleware1继续执行console。log(6)等,输出序列为1.6.2.5.3.4Promise虽然嵌套可以实现中间件流程,但是嵌套代码会造成可维护性和可读性问题,以及中间件扩展的问题。Koa.js中间件引擎由koa-compose模块实现,是Koa.js实现洋葱模型的核心引擎。koa-compose实现this.middleware=[];使用(fn){this.middleware.push(fn);...}callback(){constfn=compose(this.middleware);...}functioncompose(middleware){returnfunction(context,next){让index=-1returndispatch(0)functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()多次调用'))index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()try{returnPromise.resolve(fn(上下文,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}}}Koa实现的代码非常简洁。使用use时,我们将中间件存储在一个数组中。拦截请求时执行回调方法。在回调中调用Compose。compose方法使用递归执行中间件。遍历完成后返回promise.resolve()。实际最终执行的代码也是上面提到的promise嵌套的形式。扩展:Await和Generator我们通常说的await会阻塞后续操作,等待promise的resolve返回值或其他值。如果没有await的语法糖,怎么实现呢?这个等待过程是如何控制的?GeneratorGenerator实际上是一个特殊的迭代器letgen=null;function*genDemo(){console.log(1)yieldsetTimeout(()=>{console.log(3);gen.next();//c},100)console.log(4)}gen=genDemo();//agen.next();//巴。调用生成器,函数没有执行,也就是还没有输出1,return指向内部状态的遍历对象。b.generator函数开始执行,输出1,遇到第一个yield表达式停止,调用gen.next()返回一个对象{value:10,done:false},其中value表示setTimeout的一个标识值,即,调用clearTimeout的参数是一个数字。done表示遍历还没有结束。100毫秒后输出3;C。Generator函数从上次yeild停止的地方开始执行,直到函数结束(没有其他yield),输出4,返回{value:undefined,done:true},表示遍历结束。可以看到yeild有控制代码执行进度的功能。它类似于等待吗?查看await编译成生成器形式的代码。虽然代码多了一些,但是我们可以把_asyncToGenerator(function*(){……}调用生成器,把asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value);想成gen.next();很容易理解。functionasyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{varinfo=gen[key](arg);varvalue=info.value;}赶上(错误){拒绝(错误);返回;}if(info.done){resolve(value);}else{Promise.resolve(value).then(_next,_throw);}}function_asyncToGenerator(fn){returnfunction(){varself=this,args=arguments;returnnewPromise(function(resolve,reject){vargen=fn.apply(self,args);function_next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value);}函数_throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err);}_next(undefined);});};}constmiddleware1=/*#__PURE__*/(function(){var_ref=_asyncToGenerator(function*(ctx,next){console.log(1);yieldnext();console.log(6);});返回函数中间件1(_x,_x2){返回_ref.apply(这个,参数);};})();constmiddleware2=/*#__PURE__*/(function(){var_ref2=_asyncToGenerator(function*(ctx,next){console.log(2);yieldnext();console.log(5);});返回函数middleware2(_x3,_x4){return_ref2.apply(this,arguments);};})();constmiddleware3=/*#__PURE__*/(function(){var_ref3=_asyncToGenerator(function*(ctx,next){console.log(3);yieldnext();console.log(4);});返回函数middleware3(_x5,_x6){return_ref3.apply(this,arguments);};})();Promise.resolve(middleware1(context,/*#__PURE__*/_asyncToGenerator(function*(){returnPromise.resolve(middleware2(context,/*#__PURE__*/_asyncToGenerator(function*(){returnPromise.resolve(middleware3(context,/*#__PURE__*/_asyncToGenerator(fun动作*(){返回Promise.resolve();})));})));}))).then(()=>{console.log("end");});参考链接:https://chenshenhai.github.io...https://segmentfault.com/a/11...
