# 洋葱模型 和 组合函数 compose

# koa2 使用 async/await + koa-compose 来实现洋葱模型

附:经典图,表明请求与响应时中级件的执行顺序

# 洋葱模型案列 - koa

const Koa = require("koa");
const app = new Koa();

app.use(async (ctx, next) => {
  console.log("no.1中间件:1");
  await next(); // 执行下一个中间件
  console.log("no.1中间件:2");
});

app.use(async (ctx, next) => {
  console.log("no.2中间件:3");
  await next(); // 执行下一个中间件
  console.log("no.2中间件:4");
});

// response
app.use(async (ctx, next) => {
  console.log("no.3中间件:5");
  await next(); // 执行下一个中间件
  console.log("no.3中间件:6");
});

app.listen(3000);

// 执行结果
// no.1中间件:1
// no.2中间件:3
// no.3中间件:5
// no.3中间件:6
// no.2中间件:4
// no.1中间件:2

# 如何通过 koa-compose 来实现的

//koa-compose 源码
function compose(middleware) {
  if (!Array.isArray(middleware))
    throw new TypeError("Middleware stack must be an array!");
  for (const fn of middleware) {
    if (typeof fn !== "function")
      throw new TypeError("Middleware must be composed of functions!");
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    let index = -1;
    return dispatch(0);
    function dispatch(i) {
      if (i <= index)
        return Promise.reject(new Error("next() called multiple times"));
      index = i;
      let fn = middleware[i];
      if (i === middleware.length) fn = next;
      if (!fn) return Promise.resolve();
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err);
      }
    }
  };
}

# compose 组合函数案列 - lodash > flowRight

function flow(...funcs) {
  const length = funcs.length;
  let index = length;
  while (index--) {
    if (typeof funcs[index] !== "function") {
      throw new TypeError("Expected a function");
    }
  }
  return function (...args) {
    let index = 0;
    let result = length ? funcs[index].apply(this, args) : args[0];
    while (++index < length) {
      result = funcs[index].call(this, result);
    }
    return result;
  };
}

function flowRight(...funcs) {
  return flow(...funcs.reverse());
}

# compose 组合函数案列 - redux > compose

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args))
  );
}
Last Updated: 4/17/2022, 7:45:45 PM