koa2中间件机制-洋葱模型

233 阅读1分钟

洋葱模型

复用洋葱模型讲解图

image.png

中间件运行

image.png

koa2应用中间件

Koa文档 image.png 当程序运行到await next()的时候就会暂停当前程序,进入下一个中间件,处理完之后才回来继续处理

const Koa = require('koa')

const app = new Koa()

// #1
app.use(async (ctx, next)=>{
    console.log(1)
    await next()
    console.log(m1)
});
// #2
app.use(async (ctx, next) => {
    console.log(2)
    await next()
    console.log(m2)
})

app.use(async (ctx, next) => {
    console.log(3)
})

app.listen(7001)
console.log(`http://tnt.test.cn:7001`)

// 打印结果
1
2
3
m2
m1

结合源码分析

中间件的管理和next的实现

代码开始位置

1、app.listen(7001)

app.listen使用了this.callback()来生成node的httpServer的回调函数

listen(...args) {
    const server = http.createServer(this.callback())
    return server.listen(...args)
}
/**
* 这里用compose处理了一下this.middleware,这里通过createContext,创建了ctx,最后返回了handleRequest
**/
callback() {
    const fn = compose(this.middleware)
    
    if (!this.listeners('error').length) this.on('error', this.onerror)
    
    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res)
      return this.handleRequest(ctx, fn)
    }
    
    return handleRequest
}

2、middleware

app.use,将当前方法存放在数组中

this.middleware = [];
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;
}

3、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)
      }
    }
  }
}

4、dispatch函数

dispatch函数,它将遍历整个middleware,然后将contextdispatch(i + 1)传给middleware中的方法

// 1. 将context传给中间件
// 2. 将middleware中的下一个中间件fn作为next的返回值
return Promise.resolve(fn(context, function next () {
      return dispatch(i + 1)
}))

模拟实现中间件机制

待续