zoukankan      html  css  js  c++  java
  • Koa 中间件洋葱圈模型和 Express 的中间件模型比对

    express

    先来一段 express 代码

    // app.js
    
    var express = require('express');
    var path = require('path');
    
    var app = express();
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'pug');
    app.use(express.static(path.join(__dirname, 'public')));
    
    app.use('/test', function(req, res, next) {
      console.log(req.headers);
      console.log(req.body);
      console.log(req.query);
      res.header({
        'Content-type': 'application/json'
      });
      res.json({
        success: true
      });
    });
    
    module.exports = app;
    

    其 app.use 就只是把回调函数放进栈里,用 Layer 包裹,Layer 结构

    function Layer(path, options, fn) {
      if (!(this instanceof Layer)) {
        return new Layer(path, options, fn);
      }
    
      debug('new %o', path)
      var opts = options || {};
      // 处理的回调函数放这里
      this.handle = fn;
      this.name = fn.name || '<anonymous>';
      this.params = undefined;
      this.path = undefined;
      this.regexp = pathRegexp(path, this.keys = [], opts);
    
      // set fast path flags
      this.regexp.fast_star = path === '*'
      this.regexp.fast_slash = path === '/' && opts.end === false
    }
    

    调用时执行中间件的 handle,中间件里再手动执行 next 来链式执行下去,删减边界判断的逻辑,大体如下

    // handle 函数里
    proto.handle = function handle(req, res, out) {
      var idx = 0
      var stack = self.stack;
      
      next();
    
      function next(err) {
        var layerError = err === 'route'
          ? null
          : err;
    
        // no more matching layers
        // 函数出口。最后一个调用next,就从这里执行done出去
        if (idx >= stack.length) {
          setImmediate(done, layerError);
          return;
        }
    
      
        // find next matching layer
        var layer = stack[idx++];
        var match;
        var route = layer.route;
      
        var fn = layer.handle;
        fn(req, res, next);
        
      }
    }
    
    

    总结一下,app.use 就是往中间件数组中塞入新的中间件。中间件的执行则依靠私有方法 app.handle进行处理,按顺序寻找中间件,不断的调用 next 往下执行下一个中间件。

    koa

    koa 是由 compose 函数执行中间件,其实现抽离成了一个单独的包,代码简单。

    function compose (middleware) {
      // ...
      
      return function (context, next) {
        // last called middleware #
        let index = -1
        return dispatch(0)
        
        // 递归执行所有的 middleware,返回 Promise 对象。
        function dispatch (i) {
          if (i <= index) return Promise.reject(new Error('next() called multiple times'))
          index = i
          let fn = middleware[i]
          // 如果中间件 next 前的代码已经执行完了
          // 接下来 fn 就被赋值为 next 后面的代码。
          if (i === middleware.length) fn = next
          
          if (!fn) return Promise.resolve()
          try {
            // 执行下一个中间件 next 前的代码。
            return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
          } catch (err) {
            return Promise.reject(err)
          }
        }
      }
    }
    

    但比较上文 express 最大的不同并非 async/await 实现了更强大的功能。那就是 next 后还可以执行代码

    const Koa = require('koa');
    const app = new Koa();
    
    app.use(async (ctx, next) => {
      console.log('test1')
      next()
      console.log('test3')
    });
    
    app.use(async (ctx, next) => {
      console.log('test2')
      next()
      console.log('test4')
    });
    
    // response
    app.use(async ctx => {
      ctx.body = 'Hello World';
    });
    
    // 访问localhost:3000
    // 打印 test1 -> test2 -> test3 -> test4
    app.listen(3000);
    

    这就是所谓的 Koa 洋葱圈模型。

  • 相关阅读:
    P4396 [AHOI2013]作业 分块+莫队
    B1965 [Ahoi2005]SHUFFLE 洗牌 数论
    B1970 [Ahoi2005]Code 矿藏编码 暴力模拟
    B1968 [Ahoi2005]COMMON 约数研究 数论
    B1237 [SCOI2008]配对 贪心 + dp
    B1108 [POI2007]天然气管道Gaz 贪心
    B1734 [Usaco2005 feb]Aggressive cows 愤怒的牛 二分答案
    B1012 [JSOI2008]最大数maxnumber 分块||RMQ
    HAOI2007 反素数
    NOIP2009 Hankson的趣味题
  • 原文地址:https://www.cnblogs.com/everlose/p/12846759.html
Copyright © 2011-2022 走看看