koa-convert最主要的作用是:将koa1包中使用的Generator函数转换成Koa2中的async函数。更准确的说是将Generator函数转换成使用co包装成的Promise对象。然后执行对应的代码。当然该包中也提供了back方法,也可以把koa2中的async函数转换成koa1包中的Generator函数。

首先我们来看下使用Koa1中使用Generator函数和Koa2中使用的async函数的demo代码如下:

const Koa = require('koa');
const app = new Koa(); // koa1 使用generator函数的写法
app.use(function *(next) {
console.log(1111); // 1. 第一步先打印 1111
yield next;
console.log(222222); // 4. 第四步打印 222222
}); // koa2的写法
app.use(async (ctx, next) => {
console.log(3333); // 2. 第二步再打印 3333
await next();
console.log(44444); // 3. 第三部打印44444
}); app.listen(3001);
console.log('app started at port 3001...');

当我们在node命令行中使用 node app.js 命令时,然后浏览器中 输入地址:http://localhost:3001/ 访问的时候,我们可以看到命令中会分别打印 1111 3333 444444 222222.

我们再来看下,Koa源码中的application.js 代码如下:在use方法内部,代码如下:

const convert = require('koa-convert');
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;
}

如上koa2源码中的use函数,该函数有一个fn参数,首先判断该fn是不是一个函数,如果不是一个函数的话,直接抛出一个错误,提示,中间件必须为一个函数。第二步继续判断该fn函数是不是一个Generator函数,如果它是generator函数的话,就把该函数使用 koa-convert包转换成async函数。然后把对应的async函数放进 this.middleware数组中。最后返回该对象this。

这上面是koa2中的基本源码,下面我们来看看 koa-convert中的源码是如何做的呢?

const co = require('co')
const compose = require('koa-compose') module.exports = convert function convert (mw) {
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
if (mw.constructor.name !== 'GeneratorFunction') {
// assume it's Promise-based middleware
return mw
}
const converted = function (ctx, next) {
return co.call(ctx, mw.call(ctx, createGenerator(next)))
}
converted._name = mw._name || mw.name
return converted
} function * createGenerator (next) {
return yield next()
} // convert.compose(mw, mw, mw)
// convert.compose([mw, mw, mw])
convert.compose = function (arr) {
if (!Array.isArray(arr)) {
arr = Array.from(arguments)
}
return compose(arr.map(convert))
}

1. 部分源码如上,首先引入co包中的代码,要深入了解co包中源码请看这篇文章, 该co的作用是:将Generator函数转换成promise对象,然后会自动执行该函数的代码。

2. 引入 koa-compose包,要深入了解 koa-compose包,请看这篇文章. 该包的作用是:将koa包中的中间件合并,然后依次执行各个中间件。

3. convert 函数
1. 该函数有一个参数mw,首先判断该参数mw是不是一个函数,如果该mw不是一个函数的话,就直接抛出一个异常提示,该中间件必须是一个函数。
2. 判断该 mw.constructor.name !== 'GeneratorFunction' 是不是一个Generator函数,如果不是Generator函数的话,就直接返回该mw。
3. 如果它是Generator函数的话,就会执行 converted 函数,该函数有2个参数,第一个参数ctx是运行Generator的上下文,第二个参数是传递给Generator函数的next参数。
4. 最后返回 return co.call(ctx, mw.call(ctx, createGenerator(next))); co的作用是介绍一个Generator函数,然后会返回一个Promise对象,然后该Generator函数会自动执行。createGenerator函数代码如下:

function * createGenerator (next) {
return yield next()
}

因此 mw.call(ctx, createGenerator(next)),如果mw是一个Generator函数的话,就直接调用该Generator函数,返回return yield next(); 返回下一个中间件,然后使用调用co包,使返回一个Promise对象。该本身对象的代码会自动执行完。

5. 在convert函数代码中,有一句代码 mw.constructor.name !== 'GeneratorFunction' 是不是一个Generator函数。
可以如上面进行判断,比如如下代码演示是否是Generator函数还是AsyncFunction函数了,如下代码:

function* test () {};
console.log(test.constructor.name); // 打印 GeneratorFunction async function test2() {};
console.log(test2.constructor.name); // 打印 AsyncFunction

如上所有的分析是 convert 函数的代码了,该代码一个最主要的作用,判断传递进来的mw参数是不是Generator函数,如果是Generator函数的话,就把该Generator函数转化成使用co包装成Promise对象了。
4. back函数代码如下:

convert.back = function (mw) {
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
if (mw.constructor.name === 'GeneratorFunction') {
// assume it's generator middleware
return mw
}
const converted = function * (next) {
let ctx = this
let called = false
// no need try...catch here, it's ok even `mw()` throw exception
yield Promise.resolve(mw(ctx, function () {
if (called) {
// guard against multiple next() calls
// https://github.com/koajs/compose/blob/4e3e96baf58b817d71bd44a8c0d78bb42623aa95/index.js#L36
return Promise.reject(new Error('next() called multiple times'))
}
called = true
return co.call(ctx, next)
}))
}
converted._name = mw._name || mw.name
return converted
}

代码也是一样判断:
1. 判断mw是否是一个函数,如果不是一个函数,则抛出异常。
2. 判断mw.constructor.name === 'GeneratorFunction'; 如果是Generator函数的话,就直接返回该Generator函数。
如果不是Generaror函数的话,就执行 converted 方法,转换成Generator函数。同样的道理调用co模块返回一个Promise对象。

5. convert.compose函数代码如下:

convert.compose = function (arr) {
if (!Array.isArray(arr)) {
arr = Array.from(arguments)
}
return compose(arr.map(convert))
}

该函数的作用是:就是将一系列Generator函数组成的数组,直接转成Koa2中可执行的middleware形式。调用 koa-compose 包转换成中间件形式。

koa-convert源码分析的更多相关文章

  1. koa2中间件koa和koa-compose源码分析原理(一)

    koa是基于nodejs平台的下一代web开发框架,它是使用generator和promise,koa的中间件是一系列generator函数的对象.当对象被请求过来的时候,会依次经过各个中间件进行处理 ...

  2. Koa源码分析(三) -- middleware机制的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  3. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  4. Koa源码分析(一) -- generator

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: 1. Koa源码分析(一) -- generator 2. Koa源码分析(二) -- co的实现 ...

  5. koa源码分析

    最近项目要使用koa,所以提前学习一下,顺便看了koa框架的源码. 注:源码是koa2.x koa的源码很简洁,关键代码只有4个文件,当然还包括一些依赖npm包 const Koa = require ...

  6. 《分享》Koa2源码分析

    曾经在公司内部做的一起关于koa源码的分享,希望对你有帮助: koa2 源码分析整理 koa2(2.4.1版本)源码主要包含四个js,包括application.js, context.js, req ...

  7. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  8. flask源码分析

    本flask源码分析不间断更新 而且我分析的源码全是我个人觉得是很beautiful的 1 flask-login 1.1 flask.ext.login.login_required(func),下 ...

  9. 移动web app开发必备 - Deferred 源码分析

    姊妹篇  移动web app开发必备 - 异步队列 Deferred 在分析Deferred之前我觉得还是有必要把老套的设计模式给搬出来,便于理解源码! 观察者模式 观察者模式( 又叫发布者-订阅者模 ...

  10. jQuery源码分析系列(36) : Ajax - 类型转化器

    什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的 ...

随机推荐

  1. BZOJ4916: 神犇和蒟蒻(杜教筛)

    题意 求 $$\sum_{i = 1}^n \mu(i^2)$$ $$\sum_{i = 1}^n \phi(i^2)$$ $n \leqslant 10^9$ Sol zz的我看第一问看了10min ...

  2. python里文件读写操作

    文件读写操作一种基本操作,但是里面也存在很多需要注意的问题,例如字符编码.内存缓冲.指针位置等等.如果忽视这些问题就会引起很多不必要的麻烦.简单来说,文件的读写分为几个过程: 打开文件,并定义操作文件 ...

  3. 2018-11-29 VS Code英汉词典插件v0.0.6-改为TS实现, 加测试

    如前文VS Code英汉词典插件v0.0.4-驼峰下划线命名打算, 首先将JS源码改为TypeScript实现, 并添加了必要的测试. 昨天得知vue.js 3.0会用TypeScript实现, 正好 ...

  4. CSS3 Transform、Transition和Animation属性总结

    CSS3的三个与变形和动画啊相关的属性: Transform 浏览器支持情况: Internet Explorer 10.Firefox.Opera 支持 transform 属性. Internet ...

  5. Visual Studio 20周年,我和VS不得不说的故事

    Visual Studio 2017正式版已如期发布(点击这里查看发布全记录)!自去年 11 月正式宣布 Visual Studio 项目之后,微软终于正式推出了“宇宙最强集成开发环境(IDE)”的最 ...

  6. MySQL 横向表分区之RANGE分区小结

    MySQL 横向表分区之RANGE分区小结 by:授客 QQ:1033553122 目录 简介 1 RANGE分区 1 创建分区表 1 查看表分区 2 新增表分区 2 新增数据 3 分区表查询 3 删 ...

  7. windows server 2008 r2安装windows media player

    1.打开“服务器管理器”: 2.依次单击“功能” →  “添加功能”: 3.勾选“桌面体验”和“优质Windows音频视频体验”: 4.单击“安装”按钮:安装完毕,根据提示重新启动计算机即可.

  8. python语言学习--1

    第一天 item: 当字符串中出现 反斜杠'\' 时,输出时会把它当做转义字符处理,所以结果中不会出现它,若要正常输出则需要在字符串前面加上r, 我想r的意思是religion即“原始”的意思: it ...

  9. java垃圾回收机制GC

    记得第一次总结java 的GC的时候,是刚开始在课堂上学习GC的时候,那时候许老师第一节java课 课后老师说同学们可以去深入理解一下java的GC机制: 但是是花费了三四个小时,翻看了<Thi ...

  10. Spark操作parquet文件

    package code.parquet import java.net.URI import org.apache.hadoop.conf.Configuration import org.apac ...