理解Koa中间件和洋葱模型

3,932 阅读2分钟

Koa中间件

中间件本质就是一个函数

如何将函数变成一个函数

使用app.use()

const Koa = require('koa')

function test() {
    console.log('hello, world')
}

app.use(test)

app.listen(3000)
  1. 控制台启动程序 node app.js
  2. 浏览器访问localhost:3000,
  3. 控制台输出hello world

更常见的用法

// 用匿名函数 直接替换具名函数
app.use(() => {
    console.log('hello world2')
})

// output: hello world2

中间件执行顺序

app.use(() => {
  console.log('hello world')
});

app.use(() => {
  console.log('hello world2')
});

// 执行结果: 只有 hello world 没有hello world2

node 中间件执行顺序是只会执行第一个注册的中间件。

执行多个中间件

app.use((ctx, next) => {
  console.log('hello world')
  next() // 下一个中间件函数
})

app.use(() => {
  console.log('hello world2')
})

// output: hello world hello world2

koa中会默认用next代表下一个中间件

Koa 洋葱模型

我们先将上面代码改造下

app.use((ctx, next) => {
  console.log('hello world1')
  next()
  console.log('hello world2')
})

app.use((ctx, next) => {
  console.log('hello world3')
  next()
  console.log('hello world4')
})

// 很容易就能执行结果: 1 3 4 2 对吧
// 顺序是 中间件1的上 -> 中间件2的上 -> 中间件(next) -> 中间件2的下 -> 中间件1的下

有人问了,怎么跟我看到的不一样?

我们想想这样的场景,如果next里面需要调用异步的方法,怎么办?

使用async await

app.use(async (ctx, next) => {
  console.log('hello world1')
  await next()
  console.log('hello world2')
})

app.use(async (ctx, next) => {
  console.log('hello world3')
  await next()
  console.log('hello world4')
})

// so easy!!! 我们用async await 将异步转化成同步的 是不是就和上面一样简单了

为什么要用async await

中间件默认返回一个Promise

app.use((ctx, next) => {
  const a = await next()
  console.log(a)
})

app.use((ctx, next) => {
  next()
})

// output: Promise { undefined }  
// 这里a 值为undefined 的Promise,我们第二个中间件没显式返回嘛

这样改造下

app.use((ctx, next) => {
  const a = await next()
  console.log(a)
})

app.use((ctx, next) => {
  next()
  return 'hello world'
})

// output: Promise { 'hello world' }

这样,我们算完全理解了,中间件和洋葱模型了。

最后附上 传说的 洋葱图

洋葱模型