koa-compose:koa-compose则是将 koa/koa-router 各个中间件合并执行,结合 next() 形成一种串行机制,并且是支持异步,这样就形成了洋葱式模型
图解:一个洋葱来一刀
有图有真相:等到next()函数执行完成之后,才会next()后面的代码,那么洋葱心就是最后一个执行完毕的中间件,每个next就是洋葱每一层的分界线
来个中间件试试水:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(1.1);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(2.2);
});
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(3.3);
});
app.listen(3000, () => console.log('listening 3000'));
打印结果
// 1
// 2
// 3
// 3.3
// 2.2
// 1.1
开始剥洋葱~
- 接收函数,存入数组
const app = {
// 存放中间件的数组
middlewares: [],
// 存储方法,模拟使用中间件
use(fn) {
this.middlewares.push(fn)
}
};
- compose: 开始串联每个函数,使用下标递增,并递归至最后一个函数结束
app.compose = function (middlewares) {
return function () {
// 从第一个函数开始
dispath(0);
function dispath(idx) {
// 说明所有中间件都执行结束
if (idx === app.middlewares.length) return;
// 取出当前函数
const fn = middlewares[idx];
// 执行当前函数,传入next函数
fn(function next() {
// 并将下一个函数放入next中
dispath(idx + 1);
});
}
}
}
- 考虑支持异步,使用async/await
app.compose = function (middlewares) {
return async function () {
await dispath(0);
async function dispath(idx) {
if (idx === app.middlewares.length) return;
const fn = middlewares[idx];
await fn(function next() {
dispath(idx + 1);
});
}
}
}
- 测试一下
app.use(function (next) {
console.log(1);
next();
console.log(1.1)
})
app.use(function (next) {
console.log(2);
next();
console.log(2.2);
})
app.use(function (next) {
console.log(3);
next();
console.log(3.3);
});
app.compose()();
// 执行顺序
// 1
// 2
// 3
// 3.3
// 2.2
// 1.1
- 为什么要是洋葱模型
如果第一个中间件想得到后续中间件执行后的结果,那我们应该怎么办?
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
// 取出lastVal
console.log(1.1, ctx.lastVal);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(2.2);
});
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(3.3);
// 设置lastVal
ctx.lastVal = 3.3;
});
app.listen(3000, () => console.log('listening 3000'));
// 1
// 2
// 3
// 3.3
// 2.2
// 1.1 3.3
通过挂载在ctx,我们可以取到最后中间件的结果: 这应该就是洋葱模型的真谛了吧~