Table of Contents

前言

发现之前对koa的中间件的理解还不够完全,这里做点笔记。

中间件执行顺序

这里主要需要理解next函数。这个函数有几个点:

  • 调用next函数,就会触发下一个中间件的业务逻辑执行(异步),但不会离开当前中间件,如果在next之后有逻辑代码的话,会正常执行
  • 该函数调用是异步的,因此可以await等待后续的中间件执行结束返回回来
  • 以next函数为分隔线,上面是顺序执行,下面是倒叙执行,上面的由上向下,下面的由下向上
  • next函数的显示执行构成了一个中间件的调用链,如果任何一个中间件没有调用next,则链条会在当前中间件中断,后续即便注册了再多的中间件也不会被执行

看几个例子:

洋葱式的调用顺序

app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("1a");
  await next();
  console.log("1b");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("2a");
  await next();
  console.log("2b");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("3a");
  await next();
  console.log("3b");
});

最后的输出是:

1a
2a
3a
3b
2b
1b

中断的链式调用

如果某个中间件没有调用next,则后续的调用链会中断。

app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("1a");
  await next();
  console.log("1b");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("2a");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("3a");
  await next();
  console.log("3b");
});

最后的输出是:

1a
2a
1b

不使用await

如果在调用next的时候不使用await的话,当前的中间件触发下一个中间件的执行(异步)后不会等待后续中间件返回,后续的业务逻辑会立即继续执行。会打乱洋葱回调的顺序。

app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("1a");
  await next();
  console.log("1b");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("2a");
  next();
  console.log("2b");
});
app.use(async (ctx: Koa.Context, next: Koa.Next) => {
  console.log("3a");
  await next();
  console.log("3b");
});

最后的输出是:

1a
2a
3a
2b
3b
1b

结论

主要有下面几点:

  • 制作通用的中间件的时候,必须在代码中执行return next();将调用链继续下去,否则后面的中间件将会失效
  • 一般编写中间件,如果某些业务需要等其他中间件执行结束后处理则使用await next();,否则一律return next();
  • next调用返回的是一个Promise,因此也可以使用next().catch之类的方式来进行错误处理

EOF