Better

Ethan的博客,欢迎访问交流

Koa中间件机制的实现

中间件可以说是Koa的核心了,可以方便我们对功能进行组合,这里我们看看在Koa中,中间件是如何实现的

使用

在Koa中,我们通过use函数注册中间件,中间件函数有两个参数ctx和next,分别是Koa的context对象和下一个中间件的next函数,基础使用如下:

const app = new Koa();

// 为了演示,构建响应数据
var responseData = {}

app.use(async (ctx,next) => {
    responseData.name = 'Ethan'
    ctx.body = responseData
    await next() // 如果不调用next,则后续均不会执行
})

app.use(async (ctx,next) => {
    responseData.sex = 'man'
    ctx.body = responseData
    await next()
})

app.use(async (ctx,next) => {
    responseData.age = '24'
    ctx.body = responseData
    await next()
})

初探

根据使用方式大概可以知道app对象中应该有一个类似于middlewares的数组,用来存储所有的中间件函数,问题的关键是这些中间件函数并不是循环调用的,因为我们可以通过next决定后续中间件是否执行。

只关注中间件本身,忽视ctx函数,中间件大概长如下这样

async function m1(next) {
    console.log('m1');
    await next();
}
async function m2(next) {
    console.log('m2');
    await next();
}
async function m3(next) {
    console.log('m3');
    await next();
}

效果:m1通过调用next函数可以决定m2是否执行,m2通过是否调用next决定m3是否执行。很明显,我们需要构造一个next函数,调用m2和m3。

async function next2(){
    await m2(next3)
}
async function next3(){
    await m3()
}
m1(next2)

这样基本正常了,但是存在一个问题,执行到m3时,由于没有传递next函数,会报错,可是我们又没有中间件函数了,该如何处理呢,直接给定一个返回resolve的Promise。

async function last(){
    return Promise.resolve();
}

async function next3(){
    await m3(last)
}

在这里可以看出,我们需要一个createNext函数用来创建next函数,很显然我们需要参数middleware和next,具体如下:

function createNext(middleware,next){
    return async function(){
        await middleware(next)
    }
}

封装

我们封装一个compose函数,执行后可以将所有middlewares串联起来,考虑到Koa中还有ctx参数,因此compose返回一个函数接收ctx参数,代码如下:

function compose(middlewares){
    return ctx => {
        function createNext(middleware,next){
            return async function(){
                await middleware(next)
            }
        }
        // 最后的next
        async function next(){
            return Promise.resolve();
        }
        // 从后往前
        for(var i = middlewares.length -1; i >= 0; i--){
            next = createNext(middlewares[i],next)
        }
        await next()
    }
}

总结

有没有发现compose函数应用场景是在很多,第一次接触是在redux的mini实现中,也是实现中间件机制,然后在函数式编程中也深有体会,这次在Koa中又碰到啦。

虽然碰到多次,但每次都有新感觉,感觉还是要更多的实践。

实践源码:mini-Koa

Refer



留言