资讯专栏INFORMATION COLUMN

中间件的实现对比

张汉庆 / 1497人阅读

摘要:的中间件的中间件是函数,所以关键点是对中间件的方法不同,中利用的函数可以直接执行返回,而中函数不能直接执行,所以中的是把生成的迭代器进行传递,所以是迭代器,中是方法,然后生成一个大的函数,利用模块进行执行。

中间件的实现对比

我们在很多地方都用到了中间件的概念,我理解的中间件就是代码执行过程中插入一些中间过程。

1 redux中的中间件

就是普通函数的中间件,函数的嵌套执行

/**
  执行中间件,得到中间件的返回函数数组chain,然后利用compose方法,生成嵌套的执行chain
  方法的包装dispatch函数,
  中间件的形式是
  (getState, dispatch)=> next => action => {
     next(action);
  }
 */
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []
 
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    /**
    store.dispatch 就是第一个next  是last ware的next
    (...args) => {
      return ware0(ware1(ware2(last(...args))))
    }
    dispatch = ware0(ware1(ware2(last(...args))))
    所以中间件中next传入后返回的函数就是我们需要的函数形式,
    例如dispatch 需要的函数形式是 传一个action
    */
    return {
      ...store,
      dispatch
    }
  }
}
 
/**
reduceRight是数组的从右至左执行,
初始的参数是最后一个函数接受dispatch,
的到的一个action=>{
    dispatch(action);
}
形式的函数,作为参数composed
f的形式是
next=>action=>{
}
最终形成的就是
(...args) => {
    return funcs0(funcs1(funcs2(last(...args))))
}
*/


export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
 
  if (funcs.length === 1) {
    return funcs[0]
  }
 
  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

模拟这个过程

function func1 (next) {
  return function () {
    console.log("func1 start");
    next();
    console.log("func1 end");
  }
}

function func2 (next) {
  return function () {
    console.log("func2 start");
    next();
    console.log("func2 end");
  }
}

function func3 (next) {
  return function () {
    console.log("func3 start");
    next();
    console.log("func3 end");
  }
}

function App () {
  this.middlewares = []
}
App.prototype.use = function (middleware) {
  this.middlewares.push(middleware)
}
App.prototype.exec = function (data) {
  //funclist    func1(func2(func3(dispatch)))
  let funclist = this.middlewares.reduceRight(function (generate, next) {
    return next(generate)
  }, dispatch)
  funclist()
}

function dispatch () {
 console.log("dispatch")
}
let app = new App();
app.use(func1)
app.use(func2)
app.use(func3)
app.exec("exec");

模拟直接的中间件过程,express的中间件类似这个原理

function func4 (next) {
    console.log("func4 start");
    next();
    console.log("func4 end");
}

function func5 (next) {
    console.log("func5 start");
    next();
    console.log("func5 end");
}

function func6 (next) {
    console.log("func6 start");
    next();
    console.log("func6 end");
}

let middlewareList = [func4, func5, func6];

function exec() {
  let result = middlewareList.reduceRight(function (first, second){
    return function (name) {
      second(first)
    }
  },function(){console.log("last next")})
  result()
}

exec();
2 koa2的中间件

koa2中的中间件是async函数

也是用use方法添加到数组中,使用的时候重点是compose,如何组装中间件,然后返回Promise

module.exports = class Application extends Emitter {
  constructor() {
    this.middleware = [];
  }

  listen() {
    debug("listen");
    const server = http.createServer(this.callback());
    return server.listen.apply(server, arguments);
  }

  use(fn) {
    if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
    if (isGeneratorFunction(fn)) {
      deprecate("Support for generators will be removed in v3. " +
                "See the documentation for examples of how to convert old middleware " +
                "https://github.com/koajs/koa/blob/master/docs/migration.md");
      fn = convert(fn);
    }
    debug("use %s", fn._name || fn.name || "-");
    this.middleware.push(fn);
    return this;
  }

  callback() {
    const fn = compose(this.middleware);

    if (!this.listeners("error").length) this.on("error", this.onerror);

    const handleRequest = (req, res) => {
      res.statusCode = 404;
      const ctx = this.createContext(req, res);
      const onerror = err => ctx.onerror(err);
      const handleResponse = () => respond(ctx);
      onFinished(res, onerror);
      return fn(ctx).then(handleResponse).catch(onerror);
    };

    return handleRequest;
  }

};

//////koa-compose
这里从第一个中间件开始执行,传给中间件的next,就是执行下一个中间件的方法,并且还可以返回值,这里利用了async函数的返回值是promise,并且可以await promise,
等待执行,达到了写代码和同步类似的效果。

return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)       /     //从第一个中间件开始执行,async函数的返回值也是promise。
    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, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
3 koa1的中间件

koa1的中间件是generator函数,所以关键点是对中间件的compose方法不同,koa2中利用的async函数可以直接执行返回promise,而koa1中generator函数不能直接执行,所以
koa1中的compose是把generator生成的迭代器进行传递,所以next是迭代器,koa2中next是方法,然后生成一个大的generator函数,利用co模块进行执行。
co模块的执行,是对next拿到的value先进行promise化,然后再执行promise,在promise的then和catch中继续调用迭代器的next方法,执行迭代器。

app.callback = function(){
  if (this.experimental) {
    console.error("Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed.")
  }
  //经过compose和co的wrap得到一个promise
  var fn = this.experimental
    ? compose_es7(this.middleware)
    : co.wrap(compose(this.middleware));
  var self = this;

  return function handleRequest(req, res){
    res.statusCode = 404;
    var ctx = self.createContext(req, res);
    onFinished(res, ctx.onerror);
    fn.call(ctx).then(function handleResponse() {
      respond.call(ctx);
    }).catch(ctx.onerror);
  }
};

/*****************koa-compose*****************/

function compose(middleware){
  return function *(next){
    if (!next) next = noop();

    var i = middleware.length;

    while (i--) {
      next = middleware[i].call(this, next);
    }

    return yield *next;
  }
}
function *noop(){}



/*****************co模块*****************/


function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1)

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
    if (typeof gen === "function") gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== "function") return resolve(gen);

    onFulfilled();

    function onFulfilled(res) {
      var ret;
      try {
        //继续执行迭代器
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    function next(ret) {
      //如果迭代器执行完毕
      if (ret.done) return resolve(ret.value);
      //promise化value
      var value = toPromise.call(ctx, ret.value);
      //执行value
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, "
        + "but the following object was passed: "" + String(ret.value) + """));
    }
  });
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82922.html

相关文章

  • Koa v2.x 中文文档 Koa 对比 Express

    摘要:使用承诺和异步功能来摆脱回调地狱的应用程序,并简化错误处理。它暴露了自己的和对象,而不是的和对象。因此,可被视为的模块的抽象,其中是的应用程序框架。这使得中间件对于整个堆栈而言不仅仅是最终应用程序代码,而且更易于书写,并更不容易出错。 Koa 与 Express 此系列文章的应用示例已发布于 GitHub: koa-docs-Zh-CN. 可以 Fork 帮助改进或 Star 关注更新...

    summerpxy 评论0 收藏0
  • ​深度分析 | MyCat与DBLE对比性能调优

    作者简介 蓝寅,开源分布式中间件DBLE项目负责人;持续专注于数据库方面的技术, 始终在一线从事开发;对数据复制,读写分离,分库分表的有深入的理解与实践。 问题起因: 用benchmarksql_for_mysql对原生MyCat-1.6.1和DBLE-2.17.07版做性能测试对比,发现DBLE性能只到原生版MyCat的70%左右。 问题分析过程: 分析过程主要有以下内容:包括现象,收集数据,分...

    Mike617 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<