koa2 中间件里面的next到底是什么
koa2短小精悍,女人不爱男人爱。
之前一只有用koa写一点小程序,自认为还吼吼哈,知道有一天某人问我,你说一下 koa或者express中间件的实现原理。然后我就支支吾吾,好久吃饭都不香。
那么了解next的最好办法是什么, 百度,谷歌,知乎? 没错,肯定有用,我觉得最有用的是看源码和debug去理解。
先看下面的一段代码 ,会输出什么,只会输出 X-Response-Time
const Koa = require('koa'); const app = new Koa();// x-response-timeapp.use(async (ctx) => {const start = Date.now();//await next();const ms = Date.now() - start;ctx.set('X-Response-Time', `${ms}ms`);console.log('X-Response-Time', `${ms}ms`) });// loggerapp.use(async (ctx) => {const start = Date.now();//await next();const ms = Date.now() - start;console.log(`${ctx.method} ${ctx.url} - ${ms}`); });// responseapp.use(async ctx => {console.log('Hello World')ctx.body = 'Hello World'; });app.listen(3000);
然后修改成如下代码,会依次输出
const Koa = require('koa'); const app = new Koa();// x-response-timeapp.use(async (ctx, next) => {const start = Date.now(); await next(); const ms = Date.now() - start;ctx.set('X-Response-Time', `${ms}ms`);console.log('X-Response-Time', `${ms}ms`) });// loggerapp.use(async (ctx, next) => {const start = Date.now(); await next(); const ms = Date.now() - start;console.log(`${ctx.method} ${ctx.url} - ${ms}`); });// responseapp.use(async ctx => {console.log('Hello World')ctx.body = 'Hello World'; });app.listen(3000);
从上面的结果看来,发现什么没有,没有next 就没有下面的执行,可就简单的一个 await next(), 为嘛会有这种效果,这里,我首先简单说一下koa2中间件的实现原理。
这里先从 koa的使用说起
const Koa = require('koa'); const app = new Koa();
app.use(async (ctx, next) => {const start = Date.now(); await next(); const ms = Date.now() - start;console.log(`${ctx.method} ${ctx.url} - ${ms}`); });
app.listen(3000);
我们顺藤摸瓜,打开 koa里面的application.js (或者直接debug进入),
1.首先看 use ,就是push一个函数到 this.middleware
2. 再看listen, 方法里面 http.createServer(this.callBack), this.callBack返回的是 function(req,res){......}的函数,连起来就是 http.createServer(function(req,res){....}),标准的http创建服务的方法
3. 最后看callback,里面的核心方法, compose(this.middleware) 返回一个promise,处理完毕后再执行 handleResponse
这三个连起来,就是每次请求的时候,先进入callback, compose中间件,执行完毕后,接着处理请求。那剩下的重点变为 compose
use(fn) {if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');if (isGeneratorFunction(fn)) {deprecate('Support for generators will be removed in v3. ' +'See the documentation for examples of how to convert old middleware ' +'https://github.com/koajs/koa/blob/master/docs/migration.md');fn = convert(fn);}debug('use %s', fn._name || fn.name || '-');this.middleware.push(fn);return this;}
listen(...args) {debug('listen');const server = http.createServer(this.callback());return server.listen(...args);}
callback() { const fn = compose(this.middleware);if (!this.listeners('error').length) this.on('error', this.onerror);const handleRequest = (req, res) => {res.statusCode = 404;const ctx = this.createContext(req, res);const onerror = err => ctx.onerror(err);const handleResponse = () => respond(ctx);onFinished(res, onerror);return fn(ctx).then(handleResponse).catch(onerror);};return handleRequest;}
我们继续深入研究 compose,看源码,核心依旧是标粗的部分,核心的核心就是dispatch, dispatch会根据 middleware 的长度,依次执行。
'use strict'/*** Expose compositor.*/module.exports = compose/*** Compose `middleware` returning* a fully valid middleware comprised* of all those which are passed.** @param {Array} middleware* @return {Function}* @api public*/function compose (middleware) {if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')for (const fn of middleware) {if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')}/*** @param {Object} context* @return {Promise}* @api public*/return function (context, next) {// last called middleware #let index = -1 return dispatch(0)function dispatch (i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))index = ilet fn = middleware[i]if (i === middleware.length) fn = nextif (!fn) return Promise.resolve()try {return Promise.resolve(fn(context, function next () {return dispatch(i + 1)}))} catch (err) {return Promise.reject(err)}}} }
注意下面,如果 next为空,直接返回,也就出现了我们第一段代码的情况,后面的中间件就game over了。
if (i === middleware.length) fn = nextif (!fn) return Promise.resolve()
在往下分析,假定现在执行第一个fn,这个时候第一个fn是什么
return Promise.resolve(fn(context, function next () {return dispatch(i + 1)}))
这时候fn为如下,
fn = async (ctx, next) => {const start = Date.now();await next();const ms = Date.now() - start;ctx.set('X-Response-Time', `${ms}ms`);console.log('X-Response-Time', `${ms}ms`) }
与上面的参数对应关系如下
context :ctx,
next : function next(){ return dispatch(i+1)}
所以 await next() 就等于 await function next(){ return dispatch(i+1)} , 而 dispatch(i+1)就进入了下一个中间件了。
核心就是 dispatch(i+1),也就是dispatch(1) , dispatch本身返回promise, 所以你就在这里 await 。
依此类推 disptach(1) 会执行 this.middleware[1], 那个时候 fn就为 logger执行的函数,就这么推下去。
关于结束,还是 next 不存在的时候。 结果完毕后,再依次往上走。
所以执行的顺序是越先注册越后执行, 当然还得看你 await next() 放在什么位置。 因为这里我的 console.log都放在了 await的后面,都放到前面,结果如何,亲自测试一下喽。
转载于:https://www.cnblogs.com/wangchaoyuana/p/7497400.html
koa2 中间件里面的next到底是什么相关推荐
- Git 里面的 origin 到底代表什么意思?【转载】
作者:田雅文 链接:https://www.zhihu.com/question/27712995/answer/39946123 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- Git 里面的 origin 到底代表啥意思?【转】
(转自:https://www.zhihu.com/question/27712995/answer/39946123) 作者:田雅文 链接:https://www.zhihu.com/questi ...
- Git 里面的 origin 到底代表啥意思?
作者:田雅文 链接:https://www.zhihu.com/question/27712995/answer/39946123 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- 占用率_有问有答:任务管理器里面的GPU占用率到底是怎么算的?
从总体上来说,Windows 10是一个好系统,虽然我们天天戏称它为"Bug 10",但不可否认的是,从立项以来,开发团队就一直在努力为它加入新的功能,其中有不少是相当实用的,比如 ...
- 1709 ltsb 内存占用_有问有答:任务管理器里面的GPU占用率到底是怎么算的?
从总体上来说,Windows 10是一个好系统,虽然我们天天戏称它为"Bug 10",但不可否认的是,从立项以来,开发团队就一直在努力为它加入新的功能,其中有不少是相当实用的,比如 ...
- koa2中间件个人理解
首先我们要搞清楚我们使用koa框架到底要干一件什么事情 我们要干的事情就是前端向服务器发送HTTP请求来获取数据,我们要能在Koa中接收到该HTTP请求,并作处理,然后将数据返回.那么如何接收HTTP ...
- 自定义Button,复写里面的onKeyDown,不起作用
李刚的Android疯狂讲义真是"疯狂",浪费了3天时间,到底是他的代码有问题,还是怎么的不得而知. 问题描述:他的书里面第3.3基于回调事件处理Propagation的例程.是为 ...
- 手机相机里面的m_荣耀V30 PRO详细评测:Matrix Camera相机矩阵开启5G视频时代
[IT168 评测]随着手机摄像头的配置越来越高,手机上已经能实现不俗的拍照效果,甚至超越了数码相机(DC)成为了人们最常用的拍照工具.进入今年的下半年,随着5G的商用,手机厂商对于手机的摄像头功能优 ...
- Java平滑处理什么意思_photoshop画笔选项里的“平滑”到底是什么意思?
photoshop画笔选项里的"平滑"到底是什么意思?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! ...
最新文章
- 你是一个职业的页面重构工作者吗?
- 2020下半年新机最新消息_2020年下半年新机看点汇总:最看好的还是麒麟1020处理器!...
- jni java c++ 参数传递问题解决
- Linux Kernel and Android 休眠与唤醒(中文版)
- redis基础整理(转载+与python结合)
- 指数加权平均与RmsProp(转载+自己总结)以及Adagrad
- keil C对lib封装库反汇编成C语言,Keil软件“C语言”及“汇编”混编 —— 相关知识整理.doc...
- Java基础----JAVA语言的概述和开发环境的搭配
- JS一些概念知识及参考链接
- 使用SQL Server数据库指标预测应用程序问题
- css3新属性partA
- 使用jquery实现局部刷新DIV
- mongo 的逻辑存储和物理存储
- android 设置editext只能输入数字
- LoadRunner教程(22)-LoadRunner C语言脚本
- python图库图片_python爬取优美图库海量图片,附加代码,一键爬取
- 初中英语语法(011)-形容词
- 2019年6月20日工作总结---CSS篇
- MariaDB数据库的备份和还原
- 机械工程专业与c语言的联系,新工科背景下的机械专业C语言课程改革
热门文章
- c语言中如何用字母代替加减乘除的符号,c语言加减乘除代码
- php基础语法了解,PHP基础语法
- linux mysql 5.7.13 安装_mysql 5.7.13 安装配置方法图文教程(linux)
- python寻路_【PYTHON】a-start寻路算法
- 如何更新深度linux系统软件,deepin深度系统更新了哪些内容? 最新版deepin更新内容汇总...
- jquery audio没有声音_Audio-technica 铁三角 ATH-DSR7BT 头戴式蓝牙无线耳机测评报告 [Soomal]...
- 地图上制作线路的动画_R制作动画地图
- mysql 超时连接错误码_mysql链接超时错误
- 打开桌面计算机窗口闪动,电脑进去桌面就一直闪
- 风变Python6---布尔值,break,continue,pass,else等语句的学习