我们已经知道 koa2
中间件是基于async/await
实现的,其执行过程是通过next来驱动的,于是,koa2
就有了一个特殊的执行顺序,我们为这种执行顺序设定了一个模型叫–洋葱模型。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8R2luoz-1618999868300)(]
)
现在假想,你手里有一支牙签,横向穿过一个洋葱,是不是会层层穿透?从第一层进去、到第二层、第三次…然后到中间层后,再层层穿透的出,从第三层出、第二层、第一层…。其实我们的koa2中间件执行顺序也是这样的。抛开业务代码,用koa2官网的一个例子做实验
const Koa = require('koa');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
console.log('第一层洋葱 - 开始')
await next();
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
console.log('第一层洋葱 - 结束')
});
// x-response-time
app.use(async (ctx, next) => {
console.log('第二层洋葱 - 开始')
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
console.log('第二层洋葱 - 结束')
});
// response
app.use(async ctx => {
console.log('第三层洋葱 - 开始')
ctx.body = 'Hello World';
console.log('第三层洋葱 - 结束')
});
app.listen(8000);
我们执行一下上面代码。会发现打印结果是
第一层洋葱 - 开始
第二层洋葱 - 开始
第三层洋葱 - 开始
第三层洋葱 - 结束
第二层洋葱 - 结束
第一层洋葱 - 结束
看另外一个例子
const one = (ctx, next) => {
console.log('>> one');
next();
console.log('<< one');
}
const two = (ctx, next) => {
console.log('>> two');
next();
console.log('<< two');
}
const three = (ctx, next) => {
console.log('>> three');
next();
console.log('<< three');
}
app.use(one);
app.use(two);
app.use(three);
>> one
>> two
>> three
<< three
<< two
<< one
one = (ctx, next) => {
console.log('>> one');
next();----------------> two = (ctx, next) => {
console.log('>> two');
next();------------------> three = (ctx, next) => {
console.log('>> three');
next();--------------------|
console.log('<< three');<--|
}
console.log('<< two');
}
console.log('<< one');
}
three内有没有调用next()都不影响冒泡,没有调用next(),就是不会再传递给下一个中间件,此时three可以看作是一个普通的方法
one = (ctx, next) => {
console.log('>> one');
next();----------------> two = (ctx, next) => {
console.log('>> two');
next();------------------> three = (ctx, next) => {
console.log('>> three');
console.log('<< three');
}
console.log('<< two');
}
console.log('<< one');
}