什么是洋葱模型
Koa中的"洋葱模型"是指Koa处理中间的方式,这种方式与洋葱的结构类似,一层一层地包裹着.类似于这样.
我们来看这样一个简单的洋葱模型例子
const Koa = require('koa');
const app = new Koa()
const one = (ctx,next)=>{
console.log(1);
next()
console.log(2);
}
const two = (ctx,next)=>{
console.log(3);
next()
console.log(4);
}
const three = (ctx)=>{
console.log(5);
console.log(6);
}
app.use(one)
app.use(two)
app.use(three)
app.listen(3000,()=>{
console.log('3000端口启动');
})
我们的代码的执行结果是1 3 5 6 4 2.那么它是如何执行的呢?.
按照代码的执行顺序,我们第一步时执行的顺序是先执行app.use(one).在one中我们会先执行console.log(1);打印1.此时会碰到next(),这里的next()的作用会让我们去执行下一个函数,也就是代码会去执行app.use(two),在two中也遇到了next(),此时代码就会去执行app.use(three).在three中没有是没有next()的.当three执行完毕就会去执行two中的console.log(4),two执行完毕就会去执行one中的console.log(2);.这就是一个简易的洋葱模型实现.从第一个中间件开始依次执行,当最后一个中间执行完毕就会反向将之前中间件执行完毕,可以理解为一个从外到内,再从内到外的执行过程.
手写实现洋葱模型原理
看完上述的内容,我们大致了解的洋葱模型是一个从外到内,再从内到外的一个代码执行过程.那我们又该如何实现呢?
let middleware = []
middleware.push((ctx, next) => {
console.log(1);
next()
console.log(2);
})
middleware.push((ctx, next) => {
console.log(3);
next()
console.log(4);
})
middleware.push((ctx, next) => {
console.log(5);
next()
console.log(6);
})
let fn = compose(middleware)
fn() // 1 3 5 6 4 2
我们先将创建一个数组用于存放中间件.我们写了三个中间件,依次存入数组中.那么此时中间件数组就存了三个函数.现在我们需要的是打造一个compose()函数用于实现洋葱模型.
function compose(middleware) {
return function fn(context, next) {
}
}
我们是将中间件数组传入compose()函数中,我们需要执行这些函数,所以这里我们的也是需要返回一个函数.最关键一点,我们应该如何执行下一个函数呢?当我们遇到next()时,是会去执行下一个函数的.所以这里我们需要知道如何操作去执行下一个?
function compose(middleware) {
return function fn(context, next) {
return dispatch(0)
function dispatch(i) {
if (i > middleware.length - 1) return
let fn = middleware[i]
if (i === middleware.length) fn = next
const nextFn = dispatch.bind(context, i + 1)
const p = fn(context, nextFn)
return p
}
}
}
我们采用递归调用的方式去执行下一个函数.这里我们打造了一个dispatch()的函数用于实现从中间件数组中取到函数并递归.这里我们返回并调用dispatch(0),这里就代表着let fn = middleware[i]会取到中间数组中的第一个函数,此时代码执行就会先打印1,然后遇见next().就会执行下一个函数dispatch.bind(context, i + 1),依次递归下去.当if (i > middleware.length - 1) return成立时就会结束递归.i累加的长度大于数组的长度减一,就结束递归.这里还有一个点就是当我们执行到最后一个中间件数组中的函数时,最后一个函数不存在下一个函数,我们就特殊处理一下,让fn = next.
最后我们看看打印结果.