Koa 中的 next
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(6);
})
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(5);
})
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(4);
})
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
// 输出 1 2 3 4 5 6
Koa 中使用 use 函数注册中间件, 并按照注册注册顺序调用
在 application.js use 函数中可以看到, 传入的中间件被放入了一个 middleware 队列中
use (fn) {
...
this.middleware.push(fn)
...
}
在 application.js callback 函数中使用 koa-compose 来处理 middleware 队列
callback () {
const fn = this.compose(this.middleware)
...
}
接下来看看 compose 函数
// 省略了一些错误判断
function compose (middleware) {
return function (context, next) {
return dispatch(0)
function dispatch (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)
}
}
}
}
从上面的函数可以看出, next 函数是由 dispatch.bind(null, i + 1)
这句语句生成的
使用 next 则调用下一个中间件, 若不使用 next 则执行完当前中间件就会返回
并且在调用完成之后还能再返回到原来的中间件(即洋葱模型)
将上面的函数拆解并精简之后, 方便理解
const middleware = [];
function use(fn) {
middleware.push(fn);
}
function dispatch(i) {
const fn = middleware[i];
if (!fn) return
return fn(dispatch.bind(null, i + 1));
}
function compose() {
return dispatch(0);
}
use((next) => {
console.log(1);
next();
console.log(6);
})
use((next) => {
console.log(2);
next();
console.log(5);
})
use((next) => {
console.log(3);
next();
console.log(4);
})
compose()
// 输出 1 2 3 4 5 6
总结
- 中间件代码必须调用 next() 方法, 后续中间件才会执行
- 中断剩余中间件即不调用 next() 方法
- 中间件调用 next() 方法之后, 会先执行后面的中间件最后再执行当前中间件的剩余代码
- 中间件可以是 async 函数, 因为 dispatch 函数使用了 Promise 返回