木灵鱼儿

木灵鱼儿

阅读:602

最后更新:2021/02/10/ 18:46:35

koa教程2 中间件和洋葱模型

koa的中间件通过new出Koa对象的use方法进行注册,每个注册的中间件其实就是一个函数,函数接收两个参数:ctxnext

ctx是上下文对象,是中间件共用的一个对象,也就是共享的对象,我在1中间件对这个ctx添加或者处理内容,再进过2中间件时,可以获取到1所做的所有处理后的结果。

next是一个回调函数,他是运行下一个中间件的关键,如果没有next,那么运行就结束在当前中间件中。

也正是这个next,才能达到koa所提倡的洋葱模型。

const Koa = require("koa");
const app = new Koa();

app.use((ctx, next) => {
  console.log(1)
  next();
  console.log(3)

})

app.use((ctx, next) => {
  console.log(2)
  next();
  console.log(4)
})

app.listen(8080);

上面这段代码最终输出:

//1
//2
//3
//4

这也就是所谓的洋葱模型,先运行fn1的上半段,在运行fn2的上半段,由于更多的中间件,所以结束就运行fn2的下半段,在运行fn1的下半段。

深入理解

首先我知道next做了什么?

next永远会返回一个promise对象,这个fn1的next返回的是fn2抛出的promise对象。

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

app.use((ctx, next) => {
  console.log(2)
  console.log(3)
  return "abc";
})

输出a得到Promise { 'abc' },也就是说fn2 return的值成为了promise的值。

async...await...

在koa中常常给中间件使用async...await;我们了解下这两个代码做了什么?

await的功用:

  1. 对promise对象是一个求值,除了promise还可以是表达式
  2. 阻塞当前线程(异步转同步),但不是真的阻塞,只是当前线程不继续运行了,可以将空出的位置运行其他的
app.use(async (ctx, next) => {
  console.log(1)
  const a = await next();
  console.log(a);
  console.log(4)
})

app.use((ctx, next) => {
  console.log(2)
  console.log(3)
  return "abc";
})

输出a得到:"abc"

await一般用于异步转同步,比如异步请求,异步资源操作啊。

async的作用

  1. 配置await使用,使用await必须加async
  2. 在函数前面加上async,函数结果会被包装成一个promise对象
async function test() {
  return "abc";
}

console.log(test());
//Promise { 'abc' }

那么async和await的使用目的是什么?

为了保持洋葱模型,假如fn2里面运行的代码是一个异步的方法,那么很有可能是我们中间件已经运行完毕了,异步才运行结果处理,那么就无法保持koa所提倡的洋葱模型了,因为fn2中间件没有真正运行完毕。

保持洋葱模型

使用async和awite可以阻塞异步转同步,但是用法上却要注意!

const Koa = require("koa");
const axios = require("axios");

const app = new Koa();

app.use((ctx, next) => {
  console.log(1)
  next();
  console.log(4)
})

app.use(async (ctx, next) => {
  console.log(2)
  const a = await axios.get("https://www.mulingyuer.com/");
  console.log(3)
})

app.listen(8080);

上面代码中我们给中间件2使用async和await,让axios请求转为同步的,那么按道理,应该是输出:1,2,3,4的,但是实际上确实不同的。

实际输出的是:1,2,4,3

原因是因为中间2的await阻塞的他的运行,他并没有阻塞中间件1中的next运行,所以,如果要真正的保持洋葱模型,那么所有的中间件都应该使用async和await

于是乎:

const Koa = require("koa");
const axios = require("axios");

const app = new Koa();


app.use(async (ctx, next) => {
  console.log(1)
  await next();
  console.log(4)
})

app.use(async (ctx, next) => {
  console.log(2)
  const a = await axios.get("https://www.mulingyuer.com/");
  console.log(3)
})


app.listen(8080);

最终输出:

结果:1,2,3,4

为什么要保持洋葱模型

洋葱模型的保持有一个好处,就是我们在next函数的后面,永远可以得到中间件运行后的结果,next将中间件分作了两半,如果有很多个中间件,我们在第一个中间件的next后面,可以得到所有中间件运行后的结果。

这极大的提高的代码逻辑,使用时也更加方便,不然都是异步,单个处理可能很简单,那么集中处理就非常麻烦,这好处是显而易见的了。

做个例子:

const Koa = require("koa");
const axios = require("axios");

const app = new Koa();


app.use(async (ctx, next) => {
  console.log(1)
  await next();
  console.log(ctx.a);
  console.log(4)
})

app.use(async (ctx, next) => {
  console.log(2)
  const a = await axios.get("https://www.mulingyuer.com/");
  ctx.a = a;
  console.log(3)
})


app.listen(8080);

我们在中间件2中将axios获取到的内容赋值给ctx上下文对象,然后再中间件1中,next后面输出ctx.a,是可以得到axios所获取的内容的。

参数的传递也是非常方便,得力于洋葱模型。

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 602

相关文章