Koa 系列 —— Koa 中间件机制剖析

上一篇讲了怎样编写属于本身的 Koa 中间件,本篇将依据道理完成一个简朴的中间件处置惩罚函数,并对 Koa 中间件处置惩罚函数 compose 函数举行源码剖析。

1. compose 函数简朴完成

Koa 中间件采纳的是中间件洋葱模子,详细道理可见怎样编写属于本身的 Koa 中间件。实质就是将中间件嵌套实行:

function middleware0(){
  console.log('middleware0')
}
function middleware1(){
  console.log('middleware1')
}
// 将两个中间件嵌套实行
middleware0(middleware1())

固然实际上更庞杂,还要斟酌中间件的异步实行和中间件怎样举行嵌套。Koa 中异步处置惩罚在 Koa1 中运用的是 generator + co.js,在 Koa2 中运用的是 async/await,我们本次采纳 async/await 来处置惩罚异步。中间件的嵌套能够经由过程将中间件当参数通报来完成嵌套。据此我们对上面的代码举行进一步加工:

ps:Node7.6+ 支撑 async/await

async function middleware0(next){
  console.log('middleware0')
  await next()
}
async function middleware1(next){
  console.log('middleware1')
}
// 将两个中间件嵌套实行
middleware0(middleware1)

Koa 中经由过程 compose 函数对中间件的举行处置惩罚。compose 函数参数为 middleware 的数组, middleware 数组成员是经由过程 use 要领增加的中间件。下面写个简朴的 compose 函数,来完成多个中间件的处置惩罚:

async function middleware0(next){
  console.log('middleware0')
  await next()
  console.log('middleware0 end')
}
async function middleware1(next){
  console.log('middleware1')
  await next()
  console.log('middleware1 end')
}
async function middleware2(next){
  console.log('middleware2')
  await next()
  console.log('middleware2 end')
}

/**
 * @param {Array} 中间件数组
 */
function compose (middleware) {
  // 从第一个中间件最先实行
  return dispatch(0) 
  function dispatch(i){
    // 猎取第 i 个中间件
    fn = middleware[i]
    // 猎取不到中间件,则直接返回完毕
    if(!fn) return
    // 实行第 i 个中间件,并传入第 i + 1 个中间件
    return fn(() => dispatch(i + 1))
  }
}
// 实行
compose([middleware0, middleware1, middleware2])

2. 源码剖析

我们已简朴完成了一个 compose 函数,如今来看下 Koa 中源码的完成。Koa 中的 compose 函数已提取到 koa-compose 包中,个中的中心代码以下:

/**
 * @param {Array} 中间件数组
 * @return {Function}
 */
function compose (middleware) {
  // 推断是不是为数组,不是则抛出非常
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  // 推断 middleware 数组中的中间件是不是为函数,不是函数抛出非常
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }
  /**
   * 此处先不实行中间件,直接返回函数
   * 一致在表面举行非常推断,再最先实行中间件
   */
  return function (context, next) {
    let index = -1
    // 从第一个中间件最先实行
    return dispatch(0)
    function dispatch (i) {
      // 同一个中间件屡次挪用 next 抛出非常
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      // 猎取第 i 个中间件
      let fn = middleware[i]
      /**
       * 中间件实行完毕,搜检是不是有传入 next 回调函数
       * 此 next 并不是中间件实行的 next 参数
       */
      if (i === middleware.length) fn = next
      /**
       * 一切的返回都是Promise对象
       * Promise对象能够保证中间件和返回要求对象之间的实行递次
       */
      if (!fn) return Promise.resolve()
      try {
        // 实行第 i 个中间件,并传入第 i + 1 个中间件
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

经由过程剖析能够发明,源码相对于我们的完成越发健全:

  • 更完美的非常处置惩罚

    • 在实行前一致对传入参数举行搜检
    • 屡次实行 next 函数抛出非常处置惩罚等
  • 终究返回效果 Promise 化,保证中间件和全部处置惩罚函数在 Koa 中的实行递次,详细可参考下面 Koa 源码片断:
/**
 * application.js
 * fnMiddleware(ctx) 就是 compose 函数返回的函数,默许不传入 next 参数
 * Promise 保证中间件,handleResponse 实行递次。
 */
fnMiddleware(ctx).then(handleResponse).catch(onerror)

3. 小结

从最最先的编写 Koa 中间件,到如今浏览 compose 函数源码,Koa 中间件机制并不庞杂,相识以后,我们能够运用、编写更适宜的中间件,构建本身的 Koa 运用。

  • 本文首发于民众号,更多内容迎接关注我的民众号: 阿夸座谈
    原文作者:阿夸AQUA
    原文地址: https://segmentfault.com/a/1190000017950024
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞