聊聊方法复合Compose, Redux和Koa中的中间件机制.

648 阅读2分钟

简介.

redux和koa中都支持中间件的机制. 但两者中间件略有不同.

  • redux: 中间件的函数都是同步的,挨个遍历执行即可.
  • koa: 异步的函数,是一个promise,我们要支持async + await的中间件,所以我们要等异步结束后,再执行下一个中间件。

同步的方法复合.

我先定义三个简单的函数, 用来做例子.

// 取绝对值.
const abs = val => Math.abs(val);

// +10
const add = val => val + 10;

// 平方
const pow = val => Math.pow(val, 2);

如果我要实现这样的一个功能: 先取绝对值, 再加10,再平方. 调用方式如下:

pow(add(abs(-10)))
// 输出: 400

这种写法, 显然不太合适, 非常的不雅. 而且当函数的个数不定时, 便无法调用了. 正确的姿态是中间件机制. 将所有的中间件复合成一个超级函数. 最后执行这个超级函数即可.

// 合并成一个超级函数.
const composeFn = compose([abs, add, pow]);

// 执行超级函数.
console.log(composeFn(-10)); 

我们利用Array.reduce方法就很容易实现.

function compose(middlewares) {
    if (!middlewares || middlewares.length === 0) {
      return val => val;
    }
    
    if (middlewares.length === 1) {
      return middlewares[0];
    }
    
    // 右边的方法包括左边的方法. 最后形成一个超级方法.
    return middlewares.reduce((left, right) => {
      return (...args) => right(left(...args));
    });
}

redux的中间件机制, 正是这种实现.

异步的方法复合.

我先定义三个简单的函数, 用来做例子. 模拟koa的中间件使用.

const p1 = async (ctx, next) => {
    ctx.body = '1';
    setTimeout(() => {
      ctx.body += '2';
    }, 2000);
    
    await next();
    ctx.body += '3';
};

const p2 = async (ctx, next) => {
    ctx.body += '4';       
    await next();
    ctx.body += '5';
};

const p3 = async (ctx, next) => {
    ctx.body += '6';
};

每个函数都是一个pormise, 我们要等异步结束后,再执行下一个中间件. 正确的调用姿态是:

setTimeout(async () => {
    // 复合成一个超级方法.
    const composeFn = compose([p1, p2, p3]);
    const ctx = {};
    
    // 执行复合后的方法.
    await composeFn(ctx);
    
    // 输出: 146532
    console.log(ctx);
});

异步的复合方法合成, 代码如下:

function compose(middlewares) {
    // 方法复合, 返回的是一个复合后的方法.
    return ctx => {
       const dispatch = (i) => {
        // 拿出当前要执行的方法.
        const current = middlewares[i];
    
        // 定义要执行的下一个方法.
        const next = () => dispatch(i + 1);
    
        if (!current) {
          return Promise.resolve();
        }
    
        // promise完成后,再执行下一个
        return Promise.resolve(current(ctx, next));
      };
    
      // 执行第0个.
      return dispatch(0);
    };
}

这正是koa的中间件机制.

代码

demo