这是洋葱模型递归版,如何用循环方式实现?

ttwtdxp
  • 11

迭代版洋葱模型的实现

// 函数集(相当于中间件集合)
let arr = [
  (next) => { console.log('a1'); next(); console.log('a2'); }, // 模块A
  (next) => { console.log('b1'); next(); console.log('b2'); }, // 模块B
  (next) => { console.log('c1'); next(); console.log('c2'); }, // 模块C
];

// 用递归实现洋葱模型
let dispatch = (i) => {
  let fn = arr[i];
  if (typeof fn !== 'function') return
  let next = () => dispatch(i + 1);
  fn(next)
}
dispatch(0)

// 运行结果 ==> a1 > b1 > c1 > c2 > b2 > a2

// 那用循环怎么实现上面的结果?

稍微解释一下"洋葱化",arr数组中的函数集合,本来按照线性执行应该输出:

a1 > a2 > b1 > b2 > c1 > c2

也就是按 模块 A - B - C 的顺序来

经过 "洋葱化" 以后代码执行的顺序变成了

a1 > b1 > c1 > c2 > b2 > a2

也就是按 模块 A中嵌入BC B中嵌入C 的顺序来

具体看"洋葱化"前后的区别:

即先在 a1和a2 中间添加了 b1,c1,c2,b2
先在A模块的 a1和a2 中间添加了 B和C模块(b1,c1,c2,b2)
在b1和b2中间添加了c1,c2
再在B模块的 b1和b2 中间添加了 C模块(c1,c2)

有种说法是所有的递归都可以用非递归的方式实现

那以上的递归版的洋葱可以用循环来替代吗?

(不借助辅助函数,等价实现)

参考: 洋葱模型的实现

回复
阅读 383
1 个回答

当然可以,我们可以手动模拟javascript调用栈的运行,不过为了简化对代码流的控制我们把next前后的代码分成before和after

type Command = {
  type: "GO" | "EXECUTE";
  index: number;
  callback: (next: () => void) => void | null;
};
type Middleware = {
  before: (next: () => void) => void;
  after: () => void;
};

class App {
  private middlewares: Middleware[] = [];
  private commands: Command[] = [];

  use(middleware: Middleware) {
    this.middlewares.push(middleware);
  }

  run() {
    const { commands, middlewares } = this;
    commands.push({
      type: "GO",
      index: 0,
      callback: null
    });

    while (commands.length) {
      const { type, index, callback } = commands.pop();
      /**
       * 不存在该任务,直接返回
       */
      if (index >= middlewares.length) {
        return;
      }

      switch (type) {
        case "GO": {
          commands.push({
            type: "EXECUTE",
            index: index,
            callback: middlewares[index].before
          });
          break;
        }
        case "EXECUTE": {
          callback(() => {
            commands.push({
              type: "EXECUTE",
              index: index,
              callback: middlewares[index].after
            });
            if (index + 1 < middlewares.length) {
              commands.push({
                type: "GO",
                index: index + 1,
                callback: null
              });
            }
          });
        }
      }
    }
  }
}

const app = new App();

app.use({
  before(next) {
    console.log("a1");

    next();
  },
  after() {
    console.log("a2");
  }
});

app.use({
  before(next) {
    console.log("b1");

    next();
  },
  after() {
    console.log("b2");
  }
});

app.use({
  before(next) {
    console.log("c1");

    next();
  },
  after() {
    console.log("c2");
  }
});

app.run();
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏