前言用过koa的应该都知道洋葱模型。Koa独特的中间件流程控制是通过洋葱模型实现的。那么什么是洋葱模型,它是如何实现的呢?怎么样?洋葱模型介绍上图是洋葱模型的经典图片。从这张图我们可以看出,洋葱模型就像一个洋葱,分成了好几层。当一个请求进入的时候,会从外面到里面依次穿过每一层,到达最里面之后,每次从里面到外面穿过,我们就可以在上面做我们需要做的操作了每一层。比如对于一个请求,koa在接收到请求时,首先需要进行鉴权,然后需要解析请求参数等,完成请求后,需要处理异常,添加请求头等,以及这些操作可以放在洋葱模型的每一层上做处理。下面的示例代码是koa的示例代码:constKoa=require('koa');constapp=newKoa();app.use(async(ctx,next)=>{console.log(1);awaitnext();console.log(2);});app.use(async(ctx,next)=>{console.log(3);awaitnext();console.log(4);});app.use(async(ctx,next)=>{console.log(5);awaitnext();console.log(6);});app.listen(8000);执行上面的代码,你会发现输出的数列是1,3,5,6,4,2,这和上面介绍的洋葱模型的执行顺序是一致的。洋葱模型的实现首先我们分析一下,上面的代码app.use传入了一个异步函数,app.use可以多次使用,当请求进入时会依次调用这些函数。这与出版有关吗?订阅者模式是一致的,所以我们先实现一个app.use来实现一个app.useexportinterfaceMiddleware{(...rest:any):Promise;}exportdefaultclassOnion{middlewares:Middleware[]=[];constructor(middlewares:Middleware[]=[]){this.middlewares=middlewares;}use(middleware:Middleware){this.middlewares.push(middleware);}}上面的代码定义了一个Onion类,通过它我们可以收集订阅函数,如下代码所示:constonion=newOnion()onion.use(async(params,next)=>{console.log(1)awaitnext()console.log(2)})发布者完成收集后订阅函数需要被触发,这时候就需要在Onion中添加一个执行函数来完善Onion。如果有朋友想到发布订阅者的实现代码,他可能会想到这样做:use(middleware:Middleware){this.middlewares.push(middleware);}execute(params:any){this.middlewares.forEach(fn=>{fn(params)})}}但以这种方式,第一个-洋葱模型的in-last-out顺序无法满足,怎么办?1.定义compose函数functioncompose(middlewares:Array){if(!Array.isArray(middlewares)){thrownewError('middlewaremustbeanarray');}for(leti=0;i{letindex=0;functiondispatch(fn:Middleware|undefined){if(!fn){returnPromise.resolve();}constnext=()=>dispatch(middlewares[++index]);returnPromise.resolve(fn(params,next));}returndispatch(middlewares[index]);};}2.执行executeexportdefaultclassOnion{middlewares:Middleware[]=[];constructor(中间件:中间件[]=[]){this.middlewares=中间件;}使用(中间件:中间件){this.middlewares.push(中间件);}执行(参数:任何){constfn=compose(this.middlewares);returnfn(params);}}通过定义componse函数,可以顺序执行中间件函数constonion=newOnion();onion.use(async(params:any,next:Middleware)=>{console.log(1);awaitnext();console.log(2);});onion.use(async(params:any,next:Middleware)=>{console.log(3);awaitnext();console.log(4);});onion.use(async(params:any,next:Middleware)=>{console.log(5);awaitnext();console.log(6);});onion.execute({});这样,我们就实现了一个简易版的洋葱模型。本文转载自微信公众号“前端有戏”,可通过以下二维码关注。转载本文请联系前端友玩公众号。