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);

然后修改成如下代码,会依次输出

Hello World
GET / - 8
X-Response-Time 1040ms
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到底是什么相关推荐

  1. Git 里面的 origin 到底代表什么意思?【转载】

    作者:田雅文 链接:https://www.zhihu.com/question/27712995/answer/39946123 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

  2. Git 里面的 origin 到底代表啥意思?【转】

    (转自:https://www.zhihu.com/question/27712995/answer/39946123) 作者:田雅文  链接:https://www.zhihu.com/questi ...

  3. Git 里面的 origin 到底代表啥意思?

    作者:田雅文 链接:https://www.zhihu.com/question/27712995/answer/39946123 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

  4. 占用率_有问有答:任务管理器里面的GPU占用率到底是怎么算的?

    从总体上来说,Windows 10是一个好系统,虽然我们天天戏称它为"Bug 10",但不可否认的是,从立项以来,开发团队就一直在努力为它加入新的功能,其中有不少是相当实用的,比如 ...

  5. 1709 ltsb 内存占用_有问有答:任务管理器里面的GPU占用率到底是怎么算的?

    从总体上来说,Windows 10是一个好系统,虽然我们天天戏称它为"Bug 10",但不可否认的是,从立项以来,开发团队就一直在努力为它加入新的功能,其中有不少是相当实用的,比如 ...

  6. koa2中间件个人理解

    首先我们要搞清楚我们使用koa框架到底要干一件什么事情 我们要干的事情就是前端向服务器发送HTTP请求来获取数据,我们要能在Koa中接收到该HTTP请求,并作处理,然后将数据返回.那么如何接收HTTP ...

  7. 自定义Button,复写里面的onKeyDown,不起作用

    李刚的Android疯狂讲义真是"疯狂",浪费了3天时间,到底是他的代码有问题,还是怎么的不得而知. 问题描述:他的书里面第3.3基于回调事件处理Propagation的例程.是为 ...

  8. 手机相机里面的m_荣耀V30 PRO详细评测:Matrix Camera相机矩阵开启5G视频时代

    [IT168 评测]随着手机摄像头的配置越来越高,手机上已经能实现不俗的拍照效果,甚至超越了数码相机(DC)成为了人们最常用的拍照工具.进入今年的下半年,随着5G的商用,手机厂商对于手机的摄像头功能优 ...

  9. Java平滑处理什么意思_photoshop画笔选项里的“平滑”到底是什么意思?

    photoshop画笔选项里的"平滑"到底是什么意思?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! ...

最新文章

  1. 你是一个职业的页面重构工作者吗?
  2. 2020下半年新机最新消息_2020年下半年新机看点汇总:最看好的还是麒麟1020处理器!...
  3. jni java c++ 参数传递问题解决
  4. Linux Kernel and Android 休眠与唤醒(中文版)
  5. redis基础整理(转载+与python结合)
  6. 指数加权平均与RmsProp(转载+自己总结)以及Adagrad
  7. keil C对lib封装库反汇编成C语言,Keil软件“C语言”及“汇编”混编 —— 相关知识整理.doc...
  8. Java基础----JAVA语言的概述和开发环境的搭配
  9. JS一些概念知识及参考链接
  10. 使用SQL Server数据库指标预测应用程序问题
  11. css3新属性partA
  12. 使用jquery实现局部刷新DIV
  13. mongo 的逻辑存储和物理存储
  14. android 设置editext只能输入数字
  15. LoadRunner教程(22)-LoadRunner C语言脚本
  16. python图库图片_python爬取优美图库海量图片,附加代码,一键爬取
  17. 初中英语语法(011)-形容词
  18. 2019年6月20日工作总结---CSS篇
  19. MariaDB数据库的备份和还原
  20. 机械工程专业与c语言的联系,新工科背景下的机械专业C语言课程改革

热门文章

  1. c语言中如何用字母代替加减乘除的符号,c语言加减乘除代码
  2. php基础语法了解,PHP基础语法
  3. linux mysql 5.7.13 安装_mysql 5.7.13 安装配置方法图文教程(linux)
  4. python寻路_【PYTHON】a-start寻路算法
  5. 如何更新深度linux系统软件,deepin深度系统更新了哪些内容? 最新版deepin更新内容汇总...
  6. jquery audio没有声音_Audio-technica 铁三角 ATH-DSR7BT 头戴式蓝牙无线耳机测评报告 [Soomal]...
  7. 地图上制作线路的动画_R制作动画地图
  8. mysql 超时连接错误码_mysql链接超时错误
  9. 打开桌面计算机窗口闪动,电脑进去桌面就一直闪
  10. 风变Python6---布尔值,break,continue,pass,else等语句的学习