Compose应用

在用 PHP 开发自己博客后台的时候,碰到一个问题:一般的后台框架都会提供中间件,给使用者自定义额外的功能,so,PHP 应该如何整合一系列的功能中间件呢?

你可能想到的实现

注册中间件的时候将其存到数组中,执行时遍历数组。

const app = new App();

// 注册中间件
// this.middlewares.push(middleware);
app.use(middleware);

// 使用中间件
// 内部方法实现
function __internal() {
  // some injections here.

  for (let i = 0; i < this.middlewares.length; i++) {
    const fn = this.middlewares[i];

    // 你可以往中间件注入一些东西
    fn(injection);
  }
}
复制代码

最简单、最笨的一种方式。来看看其他大佬怎么做的。

redux 中间件

应用了函数式编程盛行的“函数合成(compose)”。简而言之就是,将多个中间件函数合成一个函数,最后只执行一次。

redux 中间件的注册方式是一次性的,无需多次调用 .use 或其它用于注册的函数:

applyMiddleware(middleware1, middlwware2, ...);
复制代码

来看看 applyMiddleware 实现:

// applyMiddleware.js
// middlewareAPI 是我们注入中间件的一些东西
const chain = middlewares.map(middleware => middleware(middlewareAPI))

// compose 所有中间件,之后一次性调用 dispatch
dispatch = compose(...chain)(store.dispatch)
复制代码

compose 在这里不在赘述。想了解的老铁们可以去瞅下 阮一峰的函数式编程入门。

koa 中间件

将注册的中间件存到 middleware 数组中,执行时调用 compose 之后的函数。

// application.js
class Application {
  use(fn) {
    // 注册时
    this.middleware.push(fn);
  }
  callback() {
    // compose 成一个 fn 函数调用
    const fn = compose(this.middleware);
  }
}
复制代码

koa-compose 与函数式编程概念中的 compose 略有不同,采用“递归 + Promise”的方式,形成了其独特的“洋葱模型”。

// koa-compose/index.js
return dispatch(0);

function dispatch(i) {
  let fn = middleware[i];

  // 终止条件
  if (!fn) return Promise.resolve();

  // 递归调用
  // dispatch 就是往中间件注入的 next
  // 从这里我们也可以看到为什么写中间件时一定要调用 next 方法,
  // 如果不调用的话,递归不会继续,之后的而中间件也不执行,一直 pending
  return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
}
复制代码

这样也很难理解为什么 await next() 之后的代码会反序执行,来看个简单的例子:

async function foofoo() {
  console.log(2);
  await Promise.resolve();
  console.log(2);
}

async function foo() {
  console.log(1);
  await foofoo();
  console.log(1);
}

a(); // 1 2 2 1
复制代码

还是只可意会,不可言传[奸笑]。关键点在于递归

express 中间件

注册成数组(额外的实例化成 layer)形式,调用时从第一个开始执行,执行完毕后调用 next 找到第二个,第二个执行完后 next 第三个,直至最后。

express 中间件的应用其实是到 router 上面,所有我们直接跳到路由的设计:

// router/index.js
// 注册时
proto.use = function use(fn) {
  // 实例化 layer
  var layer = new Layer(options, fn);

  this.stack.push(layer);
};

// 执行时
proto.handle = function handle(req, res, next) {
  // 生成引用。要注意 this 这个坑
  var stack = this.stack;

  next();

  // 这个 next 就是我们注入了中间件里
  function next(err) {
    while (match !== true && idx < stack.length) {
      layer = stack[idx ++];
      match = matchLayer(layer, path);

      // 如果你调用 next(1) 的话,这里会一直不匹配!
      // 最后直接跳过后面的中间件返回 1
      if (layerError) {
        match = false;
        continue;
      }
    }

    // 找到后
    // 其实内部也就是执行我们的 middleware 函数
    return layer.handle_request(req, res, next);
  }
};

复制代码

跟 for 循环很像,但是又依赖于开发者手动 next 触发下一个。

so ?

讲了半天中间件,和我要的 compose 有半毛钱关系哦。

  1. redux 用到了精髓
  2. koa 有点皮毛东西
  3. express 八竿子打不着

强行 pick 一波:如果你还在古老地循环调用一系列函数,不妨 compose 试下。

转载于:https://juejin.im/post/5ca48cbd6fb9a05e1c4cfc0e

你可能感兴趣的:(Compose应用)