koa2-compose学习


本文总阅读量

最近在一点一点学习koa源码和深入浅出Node.js那本书,因为现在egg也是基于koa2的,而且自己以前做毕设的时候也用过,async/await真的是太好用了;现在新的中间层用egg搭建,所以想彻底把这部分的东西好好系统看一遍。

从开始看先看到了koa的一个构造函数,然后根据Node原生的写法看,它的真正创建服务的代码在listen()当中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
constructor() {
super();

this.proxy = false;
this.middleware = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
console.log(this)
}

listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}

在看深入浅出Node.js那本书里面,知道具体的request和response都是在这个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);

const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};

return handleRequest;
}

在这里其实就是返回了一个handleRequest(),继续看:

1
2
3
4
5
6
7
8
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

到这里基本就能看出来它首先执行的就是这个fn,而这个fn又是怎么执行的?

大家都知道它是洋葱式的处理,然后看了一下是放在一个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
 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
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}
}

上面几行都是异常处理,真正的逻辑都在这个return的dispatch(0)当中,然后你发现它就是第一个中间件,首先先去做完一部分第一个中间件的事情,然后到了await next()就要去执行第二个,最终就是一个类似洋葱的模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.resolve(function(context, next){
//中间件一第一部分代码
await/yield Promise.resolve(function(context, next){
//中间件二第一部分代码
await/yield Promise.resolve(function(context, next){
//中间件三第一部分代码
await/yield next() // ...n个
//中间件三第二部分代码
}())
//中间件二第二部分代码
}())
//中间件一第二部分代码
}())

显而易见这个就是一个洋葱模型,这就是一个简单的koa的中间件调用的实现方式,感觉就可以理解成一个Promise.all()

感觉看完这个,也就对koa的中间件有了一个彻底的认知和理解,那么直接去学习中间件的源码也轻松了很多。

目录

Proudly powered by Hexo and Theme by Lap
本站访客数人次
©2016 - 2018 BosenY