两分钟搞懂从函数组合到中间件实现

2,674 阅读2分钟

很多JS框架中都会用到中间件,形成一个洋葱圈结构,这样可以很好的应付业务逻辑中的对于切面描述的需要。 经典案例比如Redux 中间件和Koa 中间件

1.无组合状态

async function fn1(next){
    console.log('fn1')
    console.log('end fn1')
}

async function fn2(next){
    console.log('fn2')
    console.log('end fn2')
}

function fn3(next){
    console.log('fn3')
}

fn3(fn2(fn1()))

这个其实不能叫做组合 只是程序先运行最内层的表达式 然后向外运行 实际上这个执行顺序就是 fn1 -> fn2 -> fn3 执行结果如下:

fn1
end fn1
fn2
end fn2
fn3

当然这个里面你即使写await执行顺序也没有什么变化

(async () => {
    await fn3(await fn2(await fn1()))
})()

2.两个函数的组合

下面我们通过参数传递的方式把两个函数组合成一个函数。

async function fn1(next){
    console.log('fn1')
    next && await next()
    console.log('end fn1')
}

async function fn2(next){
    console.log('fn2')
    next && await next()
    console.log('end fn2')
}

fn2(fn1)

执行结果如下

fn2
fn1
end fn1
end fn2

3 多个函数的组合

影响多个函数不能串行的原因是 fn2的中不能返回函数结果。 解决这个问题的方法是不要函数的运行结果而是将函数科里化后返回

async function fn1(next){
    console.log('fn1')
    next && await next()
    console.log('end fn1')
}

async function fn2(next){
    console.log('fn2')
    next && await next()
    console.log('end fn2')
}
async function fn3(next){
    console.log('fn3')
    next && await next()
    console.log('fn3')
}

async function fn4(next){
    console.log('fn4')
    next && await next()
    console.log('fn4')
}
fn1(() => fn2(() => fn3(fn4)))

看一下执行结果

fn1
fn2
fn3
fn4
fn4
fn3
end fn2
end fn1

4.通用异步Compose函数

最后我们将这个操作写成一个通用函数

const compose = (...args) =>  {
    [first,...others] = args.reverse()
    let ret = first
    others.forEach(fn => {
        let retOld = ret
        ret = () => fn(retOld)
    })
    return ret
}

compose(fn1,fn2,fn3,fn4)()