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

Koa源码阅读-中间件原理分析

时间:2023-04-03 17:56:33 Node.js

网上有很多分析Koa源码阅读的bolg项目,但是我还是想写,因为每个人的表达语言不一样,每个人的侧重点也不一样。写这篇文章希望不需要看源码的人也能一眼看懂。所以,我们开始下载koa,启动koa官方开源项目koa文档//安装npminstallkoa没有例子,所以我们自己搭建一个(我觉得这是很重要的一步,很多人没写)通过package.json"main":"lib/application.js",我们知道lib文件夹下的application.js是入口,只要导入,我们就可以导入整个koa,新建索引。jsinputonthefirstlayerofKoa//putrequire('koa')到相对路径文件中constKoa=require('./lib/application.js');constapp=newKoa();//loggerapp.use(async(ctx,next)=>{console.log(1)awaitnext();console.log(5)constrt=ctx.response.get('X-Response-Time');console.log(`${ctx.method}${ctx.url}-${rt}`);});//x-response-timeapp.use(async(ctx,next)=>{console.log(2)conststart=Date.now();awaitnext();console.log(4)constms=Date.now()-start;ctx.set('X-Response-Time',`${ms}ms`);});//responseapp.use(asyncctx=>{console.log(3)ctx.body='HelloWorld';});app.listen(3000);在命令行输入nodeindex.js打开项目,访问http://localhost:3000/终端打印1,2,3,4,5这是洋葱模型的第一题。洋葱模型是如何实现的?问题二,洋葱模型有什么好处?koa是一个非常迷你的文档,源码也非常小,只有4个js文件(Application.js,context.jsrequest.js,response.js)因为主线是讲解中间件,只讲解中间件相关类ApplicationextendsEmitter{constructor(){super();//将中间件定义为数据类型this.middleware=[];}listen(){debug('listen');constserver=http.createServer(this.callback());返回server.listen.apply(服务器,参数);}//将所有异步函数放入this.middleware数组use(fn){this.middleware.push(fn);归还这个;}callback(){constfn=compose(this.middleware);if(!this.listeners('error').length)this.on('error',this.onerror);consthandleRequest=(req,res)=>{res.statusCode=404;constctx=this.createContext(req,res);constonerror=err=>ctx.onerror(err);consthandleResponse=()=>respond(ctx);onFinished(res,onerror);fn(ctx).then(handleResponse).catch(onerror);};返回句柄请求;}}执行app.listen(3000);calllisten(),listen()callcallback()app.use(),把所有的异步函数放在this.middlewarearraycallback()//将一个包含多个异步函数的数组组合成一个函数fn=compose(this.middleware)我们打开node_modules下的koa-compose文件index.js,核心就是这个函数constPromise=require('any-promise')functioncompose(middleware){//中间件必须是数组if(!Array.isArray(middleware))thrownewTypeError('Middlewarestackmustbeanarray!')//中间件数组必须是函数for(constfnofmiddleware){if(typeoffn!=='function')thrownewTypeError('中间件必须由函数组成!')}/***@param{Object}context*@return{Promise}*@apipublic*/returnfunction(context,next){//最后调用的中间件#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()try{returnPromise.resolve(fn(上下文,下一个函数(){returndispatch(i+1)}))}catch(err){returnPromise.reject(err)}}}}compose有点乱,我们慢慢分析compose返回一个函数,这个函数返回一个立即执行的函数dispatch(0)这个就执行了,特别强调一下,然后dispatch(i)返回一个Promise。一般我们这样使用promise:constpromise=newPromise(function(resolve,reject){//...一些代码if(/*异步操作成功*/){resolve(value);}else{reject(错误);}});这个也可以用leta=()=>{console.log(1)return1}awaitPromise.resolve(a());返回函数执行的结果,如果函数没有返回那么它是未定义的constfn=compose(this.middleware);fn(ctx).then(handleResponse).catch(onerror);fn是最外层的returnLayer函数,执行fn时,输入(ctx)返回promisereturnfunction(context,next){//最后调用的中间件#letindex=-1returndispatch(0)//递归函数dispatch(ifrom0)){index=i//获取中间件的第i个异步函数letfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()尝试{返回承诺。解决(fn(上下文,函数next(){//等待下一个Promise结果returndispatch(i+1)}))}catch(err){returnPromise.reject(err)}}}在我们的例子中有3个异步函数,中间件的长度of.length为3,第一个和第二个next通过,第三个没有,所以i=2结束没有走这里如下if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()返回dispatch(i+1)所以awaitnext();等待下一个异步函数的PromisereturnPromise.resolve(fn(context,functionnext(){//等待下一个Promise的结果returndispatch(i+1)}))fn有两个参数。第一个是上下文,第二个是下一个。函数返回Promise,返回异步函数的执行结果app.use(async(ctx,next)=>{console.log(1)letc=awaitnext();//这里可以得到值111111console.log(c)console.log(5)constrt=ctx.response.get('X-Response-Time');console.log(`${ctx.method}${ctx.url}-${rt}`);});//x-response-timeapp.use(async(ctx,next)=>{console.log(2)conststart=Date.now();leta=awaitnext();console.log(4)//此处返回数字return111111constms=Date.now()-start;ctx.set('X-Response-Time',`${ms}ms`);});第三个函数执行时,返回promise,结果是undified,然后执行第二个异步函数,最后执行第一个总结:compose函数写的真奇葩,tj是个天才洋葱模型(中间件)有什么意义?不用写回调,感觉有点办法。await的好处是不用写回调,不用牵强附会。优点是开头给的例子:比如开发一个服务端处理时间/日志中间件:请求记录开头的时间conststart=Date.now();请求回来并再次记录时间constms=Date.now()-start;统计请求时间和解耦非常方便。代码是写出来给人看的,顺便让机器去执行。koa的优势不是可以实现更强的功能,而是可以更简单的完成功能。

猜你喜欢