当前位置: 首页 > 后端技术 > Node.js

Koa洋葱模型_0

时间:2023-04-03 23:56:34 Node.js

微信公众号:【前端一锅煮】一点技术,一点思考。前言:Koa是一个全新的Web框架,由Express背后的原团队打造,致力于成为Web应用和API开发领域更小、表现力更强、更健壮的基石。通过利用异步函数,Koa可以帮助您放弃回调并大大增强错误处理。Koa没有捆绑任何中间件,而是提供了一套优雅的方法来帮助您快速愉快地编写服务器端应用程序。什么是洋葱模型?先看一个demo:constKoa=require('koa')constapp=newKoa()//application//middleware1app.use((ctx,next)=>{console.log(1)next()console.log(2)})//中间件2app.use((ctx,next)=>{console.log(3)next()console.log(4)})app.listen(9000,'0.0.0.0',()=>{console.log(`Serverisstarting`)})浏览器输入localhost:9000,控制台会打印如下:1342很明显,在koa中间件中,通过next函数,划分中间件分为两部分,next的上半部分先执行,下半部分在后续所有中间件调用之后执行。koa中间件执行顺序:外层中间件进行预处理(next之前的逻辑);调用next,将控制流传递给下一个中间件,等待其完成,直到没有中间件或下一个函数执行;完成后,逐层执行各个中间件的后处理(logicafternext)。自定义实现从本质上讲,洋葱模型实际上是一个执行以下操作的函数调用方法。异步函数f(){console.log(1);awaitf2()console.log(2);}asyncfunctionf2(){console.log(3);等待f3()console.log(4);}asyncfunctionf3(){console.log(5);console.log(6);}f()output:135642到这里你会发现实际效果是实现了这样一个结构。函数f(){console.log(1);newPromise((resolve)=>{console.log(3);newPromise((resolve)=>{console.log(5);console.log(6);})console.log(4);})console.log(2);}f()从第一个父函数开始,往里面塞子函数,子函数塞孙子函数。这时候父函数调用的时候会按照顺序,先执行同步代码,将异步代码放入队列,等同步代码执行完再取出队列中的代码执行,这本质上是利用了js的事件循环机制。我们来看看简化版的实现:constmiddleware=[]letf1=asyncfunction(next){console.log(1)awaitnext()console.log(2)}letf2=asyncfunction(next)){console.log(3)awaitnext()console.log(4)}letf3=asyncfunction(next){console.log(5)console.log(6)}functionuse(fn){middleware.push(fn)}use(f1)use(f2)use(f3)//核心代码functioncompose(){returndispatch(0)functiondispatch(i){letfn=middleware[i]if(!fn)returnreturnfn(dispatch.bind(null,i+1))}}compose()从第一个函数开始,依次插入下一个函数作为参数。koa-compose源码下面是Koa洋葱模型的源码:fnofmiddleware){if(typeoffn!=='function')thrownewTypeError('Middlewaremustbecomposedoffunctions!')}//传递对象上下文并返回Promisereturnfunction(context,next){//last被调用的中间件#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)}}}}代码的两个核心点:将context一路传递到中间件并使用中间件中的下一个中间件fn作为下一个next的返回值另一种实现方法functioncompose(middlewares){returnasync(ctx)=>{functioncreateNext(middleware,oldNext){returnasync()=>{awaitmiddleware(ctx,oldNext)}}letlen=middlewares。lengthletnext=async()=>{returnPromise.resolve()}for(leti=len-1;i>=0;i--){letcurrentMiddleware=middlewares[i]next=createNext(currentMiddleware,next)}awaitnext()}}从最后一个包装中间件一、每次将自己的执行函数封装到next中,作为上一个中间件的next参数,这样循环到第一个中间件时,只需要执行一次next()就可以链式递归调用所有中间件

猜你喜欢