function compose (middleware) {
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
return function (context, next) {
let index = -1
return dispatch(0)
function dispatch (i) {
index = i
let fn = middleware[i]
if (!fn) return Promise.resolve()
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
}
}
}
上篇文章写了如何阅读Koa的源码, 粗略的过了一下Koa的源码, 但是作为一个没有得出一个具体的结论, 中间件的运行原理也不清楚, 这里我们再仔细的过一遍Koa的源码.
跟着例子过一遍
首先还是先过一遍例子
起一个web服务, 来一个Hello World, 作为http模块的再封装, 我们还是慢慢来挖掘它是如何封装的吧(无关的代码我都会删掉).
首先是
listen
:http模块我们都知道 无非是
http.createServer(fn).listen(port)
, 其中fn带着req, res. 根据上面的封装我们可以肯定this.callback
肯定是带着请求以及进行响应了. 那么再来看看this.callback
吧.果然
callback
返回的函数是带着req, res的, 那我继续往下走看handleRequest
究竟经历了什么,ctx
大佬出现了, 我们在用koa的时候所有请求响应都是挂在ctx上的, 看起来ctx是通过createContext
创建的, 那就继续看createContext
吧:createContext
比较简单, 就是把各种有用的没用的变量挂到context上, 代码也很简单, 但是因为涉及到request和response我们需要简单看一下request.js和response.js:都是很简单获取变量没啥好说的, 那么回到前面callback部分, ctx创建好了然后调用并返回了
this.handleReques
, 没啥好说的, 继续看呗:这一部分略微复杂一点, 由上面看出来
fnMiddleware
是我们取出来的中间件, 然后我们把ctx传到中间件里执行, 跟我们的通常用法有点像了. 到这一步重点来了: 中间件中间件
在探究中间件的原理之前, 不妨先来看看中间件是怎么用的, 来个简单的例子:
上面的结果很明确了, 但是我们不妨来可视化一下:
respond: ctx.body是Hello world呢 就糊弄一下用户返回吧
看到没, ctx.body不代表立即响应, 仅仅是一个我们后面会用到的变量, 也就是说我们的ctx过了一遍所有的中间件然后才会做出响应. 这里不提await神奇的暂停效果, 我们就需要可以这么用就行了. 那么我们这个中间件是怎么实现的呢, 来看compose.js:
看过我前一篇的可以知道这里其实就是一个递归. 但是跟connect的递归不一样这里是Promise, 我们都知道await 跟Promise搭配味道更佳嘛. 重点是这个next, 我们调用了await next之后, 程序非得等这个Promise执行完不可, 我们来简化一下中间件的模型:
是不是这样一下就清楚了, 作为应用层的东西, 我们不需要去考虑async/await究竟是怎么实现的, 只需要了解它实现了什么样的效果.
还是得佩服tj大神. 有问题可以互相交流哈.