Kivi

没有什么远大理想,只是永远都不会满足而已


  • 首页

  • 关于

  • 标签

  • 归档

Koa2源码初读

发表于 2016-05-25 更新于 2017-07-02 分类于 Node.js 阅读次数:
本文字数: 3.6k 阅读时长 ≈ 3 分钟

koa context

在分析koa其他原理之前,首相需要理解koa中提出的context到底是什么,下面用一张图来说明

koa

抽象和封装

  • 抽象出Request, Response, Context三大类,封装常用操作

  • 将Request,Response,部分方法代理到Context上

    为了实现最终用一个`Context`实例携带全部属性方法,传递给中间件使用
    
  • 实例化Request,Response,Context,并将其实例关联Koa Application实例(示例图中的app),Node.js的http request实例(示例图中的req),Node.js的http response实例(示例图中的res)关联起来

    关联的部分原因是`Request`, `Response`, `Context`封装常用操作时,用到了`app`,`req`,`res`
    另一部分原因是保留原始`req`,`res`数据
    

这样一来,Context的实例就将一个http server常用的全部属性方法全部封装到一个上下文中,方便传递给中间件调用,完成了请求准备工作

koa middleware

启动过程分析

1
2
3
4
5
listen() {
debug('listen');
const server = http.createServer(this.callback());
return server.listen.apply(server, arguments);
}

从上面可以看出,callback函数完成了所有的请求处理工作,这里是callback源码

1
2
3
4
5
6
7
8
9
10
11
12
callback() {
const fn = compose(this.middleware);

if (!this.listeners('error').length) this.on('error', this.onerror);

return (req, res) => {
res.statusCode = 404;
const ctx = this.createContext(req, res);
onFinished(res, ctx.onerror);
fn(ctx).then(() => respond(ctx)).catch(ctx.onerror);
};
}

启动的过程:

  • compose中间件
  • 生成Node.js原生请求处理函数
    创建上下文实例ctx(也就是生成`koa context`实例)
    执行中间件函数
    

创建上下文中间件,也就是生成Context实例对象,上文已经详细描述过
执行中间件函数,这行代码就是执行compose中间件之后返回的Pormise对象,所以我们把重点放在compose的过程上

compose中间件

const fn = compose(this.middleware);

简单来说,这行代码的作用就是将所有的中间件有序组合,成为一个Promise对象返回,compose函数来自koa-compose。这里是源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
'use strict'

const Promise = require('any-promise')

/**
* 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 = i
const fn = middleware[i] || next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}
}
  • 首先看返回值,返回的是一个中间件函数function (context, next),我们从前面koa callback函数的这行代码中可以观察到,(fn就是compose返回的中间件函数)一次fn的调用就结束了整个请求,所以fn最终的作用就是依次调用所有的中间件

    1
    fn(ctx).then(() => respond(ctx)).catch(ctx.onerror);
  • 然后是dispatch函数,这个函数的返回值是一个Promise对象(Promise.resolve和Promise.reject是将一个现有对象转化为一个promise对象,状态不同),这个Promise对象是由原始的中间件函数构造而成的。

    原始的中间件函数就是传递给`app.use()`的函数
    
  • 将上下文context和next函数传递给原始中间件对象,context就是中间件函数的context,这里的next函数是递归的关键,next函数返回dispatch函数执行结果,也就是下一个被包装成Promise对象的中间件函数,这也就是为什么我们在koa2中执行完一个中间件,如果还需要执行下一个中间件的时候,需要执行await next()的原因

中间件回逆特性

先来看一张图

koa

从上面的分析我们已经知道,await next()相当于在执行下一个中间件,那么在await next()之后我们执行的操作又是什么呢?这个就是koa中间件回逆的特性,在下一个中间件执行完成之后,在http请求响应完成之前,你还可以做一些非常有用的操作,这整个过程,称之为回逆。
这是个非常有用的特性,举个非常简单的例子,用下面这段代码可以非常简单的统计出中间件处理请求的时间
1
2
3
4
5
6
app.use(async function (ctx, next) {
log.http(`${ctx.request.method} ${ctx.request.url}`);
console.time('http request time');
await next();
console.timeEnd('http request time');
});

总结

从koa2源码分析我们可以看出,koa最重要的两个知识点就是context和middleware,再实际使用过程中,深度感受这两点,一定能有更多的收获

补充

其实async函数就是generator函数的语法糖,要再深入的理解中间件的compose原理和回逆原理,还是需要koa@master源码的中去理解

参考链接

深入浅出 Koa

# javascript # Node.js # Koa # 源码
Linux端口号知多少
siege做简单压力测试
  • 文章目录
  • 站点概览
kivi

kivi

nodejs | server
58 日志
17 分类
32 标签
RSS
  1. 1. koa context
    1. 1.1. 抽象和封装
  2. 2. koa middleware
    1. 2.1. 启动过程分析
    2. 2.2. compose中间件
    3. 2.3. 中间件回逆特性
  3. 3. 总结
  4. 4. 参考链接
© 2019 kivi | 173k | 2:37
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
|