koa教程2 中间件和洋葱模型
koa的中间件通过new出Koa对象的use方法进行注册,每个注册的中间件其实就是一个函数,函数接收两个参数:ctx
和next
。
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的功用:
- 对promise对象是一个求值,除了promise还可以是表达式
- 阻塞当前线程(异步转同步),但不是真的阻塞,只是当前线程不继续运行了,可以将空出的位置运行其他的
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的作用
- 配置await使用,使用await必须加async
- 在函数前面加上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所获取的内容的。
参数的传递也是非常方便,得力于洋葱模型。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据