我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
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所获取的内容的。
参数的传递也是非常方便,得力于洋葱模型。
评论(0)