构建自己的koa,模拟koa的洋葱圈模型

494 阅读2分钟

1.同步compose

function add(a, b) {
    return a + b
}

function double(a) {
    return a * 2
}

function remove(r){
    return r-1
}

先看上边的代码, 如果给add函数传递村始参数1,2。 结果返回3, 然后将返回的结果3传递给double函数, double函数再将将返回结果6传递给remove函数。可以得到结果5。 如果用最原始的方法写, 代码如下

let addRes = add(1,2)
let dbRes = double(addRes)
let rmRes = remove(dbRes)
console.log(rmRes) // 5

//或者
remove(double(add(1,2)))   // 5

那么能不能把这种通过值与值之前互相传递的函数封装一下, 只传递函数名称和初始值, 就可以得到想要返回的结果呢。 答案是肯定的, 下面是实现的代码

function compose(mid) {
    return function (...args) {
        let res = mid[0](...args)
        for (let i = 1; i < mid.length; i++) {
            res = mid[i](res)
        }
        return res
    }
}

const middlewares = [add, double, remove]
let fn = compose(middlewares)
let final = fn(1, 2) // 5

解析: 封装一个compose函数, 参数是包含各个函数名称的数组。 将要传递的初始参数封装在闭包中。通过es6方法...args来解构赋值传递的初始参数。 先得到最初的add函数返回值mid[0](...args)。然后for循环剩余参数。 因为已经操作过了mid[0]。所以for循环以1开头。 将第一个add函数返回的值res,传递给第二个函数, 并将结果重新赋值为res, 最后返回res。

2. 异步compose

构建一个异步的compose。 根据洋葱圈模型。 下边代码的执行顺序是 fn1, fn2 2秒后打印fn 3, fn2 end, fn1 end


async function fn1(next) {
   console.log('fn1');
   await next()
   console.log('fn1 end');
}
async function fn2(next) {
   console.log('fn2');
   await delay()
   await next()
   console.log('fn2 end');
}
async function fn3(next) {
   console.log('fn3');
}

function delay() {
   return new Promise(resolve => {
       setTimeout(() => {
           resolve()
       }, 2000)
   })
}

由于是异步的,所以使用promise来处理异步。 使用递归去调用函数。

function compose(middlewares) {
    return function () {
        return dispatch(0)

        function dispatch(i) {
            let fn = middlewares[i]
            if (!fn) {
                return Promise.resolve()
            }
            return Promise.resolve(fn(function next() {
                return dispatch(i + 1)
            }))
        }
    }
}
const middlewares = [fn1, fn2, fn3]
const finalFn = compose(middlewares)