koa中间件机制详解

转自:https://cnodejs.org/topic/58fd8ec7523b9d0956dad945

koa是由express原班人马打造的一个更小、更富有表现力、更健壮的web框架。

在我眼中,koa的确是比express轻量的多,koa给我的感觉更像是一个中间件框架,koa只是一个基础的架子,需要用到的相应的功能时,用相应的中间件来实现就好,诸如路由系统等。一个更好的点在于,express是基于回调来处理,至于回调到底有多么的不好,大家可以自行搜索来看。koa1基于的co库,所以koa1利用Generator来代替回调,而koa2由于nodeasync/await的支持,所以koa2利用的是async/await。关于async以及co库等,大家可以参考我之前写过的一篇文章(理解async)。koa可以说是一个各种中间件的架子,下面就来看一下koa对于中间件部分的实现: 

koa1的中间件

koa1主要利用的是Generator来实现,一般来说,koa1的一个中间件大概是长这个样子的:

app.use(function *(next){console.log(1);yield next;console.log(5);
});
app.use(function *(next){console.log(2);yield next;console.log(4);
});
app.use(function *(){console.log(3);
});

这样的输出会是1, 2, 3, 4, 5koa的中间件的实现主要依靠的是koa-compose

function compose(middleware){return function *(next){if (!next) next = noop();var i = middleware.length;// 组合中间件while (i--) {next = middleware[i].call(this, next);}return yield *next;}
}
function *noop(){}

源码非常的简单,实现的功能就是将所有的中间件串联起来,首先给倒数第一个中间件传入一个noop作为其next,再将这个整理后的倒数第一个中间作为next传入倒数第二个中间件,最终得到的next就是整理后的第一个中间件。说起来比较复杂,画图来看:

 

实现的效果如同上图,与redux需要实现的目标类似,只要遇到了yield next就去执行下一个中间件,利用co库很容易将这个流程串联起来,下面来简单模拟下,中间件完整的实现:

 

const middlewares = [];const getTestMiddWare = (loggerA, loggerB) => {return function *(next) {console.log(loggerA);yield next;console.log(loggerB);}
};
const mid1 = getTestMiddWare(1, 4),mid2 = getTestMiddWare(2, 3);const getData = new Promise((resolve, reject) => {setTimeout(() => resolve('数据已经取出'), 1000);
});function *response(next) {// 模拟异步读取数据库数据const data = yield getData;console.log(data);
}middlewares.push(mid1, mid2, response);
// 简单模拟co库
function co(gen) {const ctx = this,args = Array.prototype.slice.call(arguments, 1);return new Promise((reslove, reject) => {if (typeof gen === 'function') gen = gen.apply(ctx, args);if (!gen || typeof gen.next !== 'function') return resolve(gen);const baseHandle = handle => res => {let ret;try {ret = gen[handle](res);} catch(e) {reject(e);}next(ret);};const onFulfilled = baseHandle('next'),onRejected = baseHandle('throw');onFulfilled();function next(ret) {if (ret.done) return reslove(ret.value);// 将yield的返回值转换为Proimselet value = null;if (typeof ret.value.then !== 'function') {value = co(ret.value);} else {value = ret.value;}if (value) return value.then(onFulfilled, onRejected);return onRejected(new TypeError('yield type error'));}});
}
// 调用方式
const gen = compose(middlewares);
co(gen);

koa2的中间件

随着node对于async/await的支持,貌似不需要再借助于co这种工具库了,直接利用原生的就好,于是koa也做出了改变,来看目前的koa-compose

function compose (middleware) {// 参数检验return function (context, next) {// last called middleware #let index = -1return 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()// 用Promise包裹中间件,方便await调用try {return Promise.resolve(fn(context, function next () {return dispatch(i + 1)}))} catch (err) {return Promise.reject(err)}}}
}

koa-compose利用了Promisekoa2的中间件的参数也有一个变为了两个,而且执行下一个的中间件利用的是await next(),要达到与上面的示例代码的相同效果,需要更改中间件的写法:

 

const middlewares = [];
const getTestMiddWare = (loggerA, loggerB) => async (ctx, next) => {console.log(loggerA);await next();console.log(loggerB);
};const mid1 = getTestMiddWare(1, 4),mid2 = getTestMiddWare(2, 3);
const response = async () => {// 模拟异步读取数据库数据const data = await getData();console.log(data);
};
const getData = () => new Promise((resolve, reject) => {setTimeout(() => resolve('数据已经取出'), 1000);
});
middlewares.push(mid1, mid2);// 调用方式
compose(middlewares)(null, response);

如何做到兼容

可以看到的是,koa1koa2对于中间件的实现还是有着很多的不同的,将koa1的中间件直接拿到koa2下面来使用肯定是会出现错误的,如何兼容这两个版本也成了一个问题,koa团队写了一个包来是koa1的中间件可以用于koa2中,叫做koa-convert,先来看看这个包怎么使用:

function *mid3(next) {console.log(2, 'koa1的中间件');yield next;console.log(3, 'koa1的中间件');
}
convert.compose(mid3)

来看下这个包实现的思路:

// 将参数转为数组,对每一个koa1的中间件执行convert操作
convert.compose = function (arr) {if (!Array.isArray(arr)) {arr = Array.from(arguments)}return compose(arr.map(convert))
}
// 关键在于convert的实现
const convert = mw => (ctx, next) => {// 借助co库,返回一个Promise,同时执行yieldreturn co.call(ctx, mw.call(ctx, createGenerator(next)));
};function * createGenerator (next) {/*next为koa-compomse中:function next () {return dispatch(i + 1)}*/return yield next()// 执行完koa1的中间件,又回到了利用await执行koa2中间件的正轨
}

个人感觉koa-convert的思路就是对Generator封装一层Promise,使上一个中间件可以利用await next()的方式调用,对于Generator的执行,利用co库,从而达到了兼容的目的。

 

 

 

转载于:https://www.cnblogs.com/duhuo/p/8526160.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252226.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何构建一个真实的推荐系统?

AI 前线导读&#xff1a;随着互联网行业的井喷式发展&#xff0c;数据规模呈现爆炸式增长。大数据中蕴含了巨大的价值&#xff0c;但同时也来了很 “信息过载” 的问题。推荐系统作为一个广泛应用的信息过滤系统&#xff0c;在很多领域取得了巨大的成功。在电子商务上&#xff…

volatile的适用场景

介绍 把代码块声明为 synchronized&#xff0c;有两个重要后果&#xff0c;通常是指该代码具有 原子性&#xff08;atomicity&#xff09;和 可见性&#xff08;visibility&#xff09;。 原子性意味着个时刻&#xff0c;只有一个线程能够执行一段代码&#xff0c;这段代码通过…

C#如何测试代码运行时间

第一种方式&#xff1a;System.Diagnostics.Stopwatch stopwatch new Stopwatch(); stopwatch.Start(); // 开始监视代码运行时间 // 需要测试的代码 .... stopwatch.Stop(); // 停止监视 TimeSpan timespan stopwatch.Elapsed; // 获取当前实例测量得出的总时间 double …

第二十二章:动画(六)

复合动画您可以混合等待和未等待的调用来创建复合动画。 例如&#xff0c;假设您希望按钮在大小扩展的同时旋转360度然后收缩。ViewExtensions类定义一个方法名称ScaleTo&#xff0c;它为Scale属性设置动画&#xff0c;就像RotateTo为Rotate属性设置动画一样。 Button大小的扩展…

ubantu之Git使用

本文讲述在Ubuntu 14.04 x64环境下&#xff0c;如何安装Git&#xff0c;配置连接GitHub&#xff0c;并且上传本地代码到github。 一. 注册Git账户以及创建仓库 要想使用github第一步当然是注册github账号了。之后就可以创建仓库了&#xff08;免费用户只能建公共仓库&#xff0…

C#如何打包EXE程序生成setup安装文件

C#如何打包EXE程序生成setup安装文件作为研发人员&#xff0c;在本机上开发的winform wpf或者控制台程序需要发给其他人测试时候&#xff0c;一般需要对其进行打包生成setup安装文件&#xff0c;今天第一次&#xff0c;搜了下资料&#xff0c;记录如下&#xff1a;注&#xff1…

最具戏剧性的分析诊断案例——十分钟锁定数据库性能“元凶”

昨天&#xff0c;正好有点空时间想看看书&#xff0c;结果&#xff0c;刚打开书&#xff0c;没看几个字儿&#xff0c;接到用户电话说&#xff1a;一个库有问题&#xff0c;希望能帮忙看下。因为我知道他们那边也有自己的专职DBA&#xff0c;于是问&#xff1a;没让人给看看吗&…

Python黑科技:在家远程遥控公司电脑,python+微信一键连接!

有时候需要远程家里的台式机使用&#xff0c;因为我平时都是用 MAC 多&#xff0c;但是远程唤醒只能针对局域网&#xff0c;比较麻烦&#xff0c;于是我想用微信实现远程唤醒机器。 *注意&#xff1a;全文代码可左右滑动查看 准备工作 本程序主要是实现远程管理 Windows10操作系…

c#通过app.manifest使程序以管理员身份运行

通常我们使用c#编写的程序不会弹出这个提示&#xff0c;也就无法以管理员身分运行。微软的操作系统使用微软的产品方法当然是有的&#xff0c;通过app.manifest配置可以使程序打开的时候&#xff0c;弹出UAC提示需要得到允许才可以继续&#xff0c;这样就获得了管理员的权限来执…

MOS管基本认识(快速入门)

1. 三个极的判定G极(gate)—栅极&#xff0c;不用说比较好认 S极(source)—源极&#xff0c;不论是P沟道还是N沟道&#xff0c;两根线相交的就是 D极(drain)—漏极&#xff0c;不论是P沟道还是N沟道&#xff0c;是单独引线的那边2. N沟道与P沟道判别箭头指向G极的是N沟道 箭头背…

基础构建模块

5 基础构建模块 Java平台类库包含了丰富的并发基础构建模块&#xff0c;例如线程安全的容器类以及各种用于协调多个相互协作的线程控制流的同步工具类(Synchronizer)。本章将介绍其中一些最有用的并发构建模块。 5.1同步容器类 同步容器类包括Vector和Hashtable&#xff0c;二者…

TCP定时器

1. TCP中7种定时器 TCP中有7中定时器 &#xff08;1&#xff09;建立连接定时器(connection-establishment timer) &#xff08;2&#xff09;重传定时器(retransmission timer) &#xff08;3&#xff09;延迟应答定时器(delayed ACK timer) &#xff08;4&#xff09;坚持定时…

软件工程网络15个人阅读作业1 201521123038 游舒婷

软件工程网络15个人阅读作业1 201521123038 游舒婷 1.博客园地址 sakurai3104 2.码云地址 sakurai3104 3.阅读与思考 &#xff08;1&#xff09;回想一下你初入大学时对网络工程专业的畅想 当初你是如何做出选择网络工程专业的决定的&#xff1f; 填报志愿的时候&#xff0c;大…

18LaTeX学习系列之---LaTeX的参考文献

目录 目录前言&#xff08;一&#xff09;简单的参考文献1.说明2.源代码3.输出效果&#xff08;二&#xff09;以文件管理的方式1.说明&#xff1a;2.源代码&#xff1a;3.输出效果&#xff08;三&#xff09;直接从源网站获取1.说明&#xff12;.操作目录 本系列是有关LaTeX的…

Python 基础(常用数据结构)

常用数据结构 1&#xff09;元组 元组是一种静态的数据结构&#xff0c;无法修改&#xff0c;若要修改只能重新生成新的元组。 输出结果&#xff1a; 元组元素的获取是通过索引值去获得的&#xff1b;例如上面的tup1[0]返回apple&#xff1b;另外你可以直接把tup1一次性赋给多个…

Java IO(二)——RandomAccessFile

一、RandomAccessFile RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类&#xff0c;它提供了众多的文件访问方法。RandomAccessFile类支持"随机访问"方式&#xff0c;可以跳转到文件的任意位置处读写数据。要访问一个文件的时候&#xff0c;不想把文件…

centos7 安装python3

1.查看是否已经安装Python CentOS 7.2 默认安装了python2.7.5 因为一些命令要用它比如yum 它使用的是python2.7.5。 使用 python -V 命令查看一下是否安装Python 然后使用命令 which python 查看一下Python可执行文件的位置 可见执行文件在/usr/bin/ 目录下&#xff0c;切换到该…

centos svn 的搭建

一. SVN 简介 Subversion(SVN) 是一个开源的版本控制系統, 也就是说 Subversion 管理着随时间改变的数据。 这些数据放置在一个中央资料档案库(repository) 中。 这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动。 这样你就可以把档案恢复到旧的版本, 或是浏…

软件建模——第3章 项目前期

3.1 项目前期的主要工作 3.1.1 现状分析 1.硬件分析 2.软件分析 3.1.2 需求收集 3.1.3 粗略设计 1.体系结构设计 2.硬件&#xff08;网络&#xff09;设计 3.应用系统设计 4.安全设计 5.配套设计 3.1.4 可行性分析 3.2 结构化的项目前期实例 3.2.1 组织分析&#xff08;自动化…

echarts_部分图表配置_图表click事件

额。。当然其他事件都是支持的&#xff0c;此文仅以click做简单介绍&#xff1a; 请点击“柱子”。。。 官方介绍&#xff1a;http://echarts.baidu.com/tutorial.html#ECharts%20%E4%B8%AD%E7%9A%84%E4%BA%8B%E4%BB%B6%E5%92%8C%E8%A1%8C%E4%B8%BA 1 function bottom_z (thisI…