洋葱模型
复用洋葱模型讲解图
中间件运行
koa2应用中间件
Koa文档
当程序运行到
await next()的时候就会暂停当前程序,进入下一个中间件,处理完之后才回来继续处理
const Koa = require('koa')
const app = new Koa()
// #1
app.use(async (ctx, next)=>{
console.log(1)
await next()
console.log(m1)
});
// #2
app.use(async (ctx, next) => {
console.log(2)
await next()
console.log(m2)
})
app.use(async (ctx, next) => {
console.log(3)
})
app.listen(7001)
console.log(`http://tnt.test.cn:7001`)
// 打印结果
1
2
3
m2
m1
结合源码分析
中间件的管理和next的实现
代码开始位置
1、app.listen(7001)
app.listen使用了this.callback()来生成node的httpServer的回调函数
listen(...args) {
const server = http.createServer(this.callback())
return server.listen(...args)
}
/**
* 这里用compose处理了一下this.middleware,这里通过createContext,创建了ctx,最后返回了handleRequest
**/
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
}
2、middleware
app.use,将当前方法存放在数组中
this.middleware = [];
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;
}
3、compose 函数 koa-compose
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, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
4、dispatch函数
dispatch函数,它将遍历整个middleware,然后将context和dispatch(i + 1)传给middleware中的方法
// 1. 将context传给中间件
// 2. 将middleware中的下一个中间件fn作为next的返回值
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
模拟实现中间件机制
待续