Express與Koa中間件機制分析(二)

前言

Express與Koa中間件機制分析(一)中咱們有提到,Express 爲線型模型,而 Koa 則爲洋蔥型模型,以前咱們已經經過解析 connect 的源碼對 Express 中間件機制進行了分析,本篇文章咱們將對 Koa 的部分源碼進行分析以幫助你們來理解其中間件機制。javascript

koa1 基於的 co 庫,因此 koa1 利用 Generator 來代替回調,而 koa2 因爲 node 對 async/await 的支持,因此 koa2 利用的是 async/awaitjava

目前你們經常使用的基本上都是 koa2,在這裏咱們先對於 Koa2 的實現進行分析,Koa1 先留個坑,以後再補充。node

Koa1 中間件

留坑待續...api

Koa2 中間件

Koa2 中間件的實現依賴於其自身的 koa-compose,下面咱們看一下 compose 函數的實現。數組

function compose (middleware) {
  // 判斷參數是否合法,middleware 要求爲數組且其中每一個數組元素都爲 function
  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
    // 遞歸返回一個函數 該函數返回一個 Promise 的對象
    return dispatch(0)
    function dispatch (i) {
      // 當 next 方法被屢次調用時會出現
      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()
      // Promise 封裝中間件 進行遞歸調用
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
複製代碼

在 Koa 中的 use 函數內,中間件入棧,在 compose 函數中傳入的 middleware 即爲中間件數組。在 compose 中對該數組進行遞歸調用,返回一個 Promise 鏈。koa

下面經過一個例子來對中間件執行這一過程進行分析:async

// 中間件 fn1 和 fn2
async function fn1 (ctx, next) {
  console.log('first: start')
  await next()
  console.log('first: end')
}
async function fn2 (ctx, next) {
  console.log('second: start')
  await next()
  console.log('second: end')
}
// 模擬中間件數組
const arr = [fn1, fn2]
// 執行函數,這裏返回的是一個 Promise 對象
compose(arr)()
// 執行後的結果
// first: start
// second: start
// second: end
// first: end
複製代碼

其實,在 compose 內部遞歸執行的操做後,造成多個 Promise 層層嵌套(以下面代碼所示),此時 next 函數其實就是下一個中間件,await 須要等待內部的 Promise ,因此其執行結果會呈現一個剝洋蔥的模式。函數

function mycompose() {
  return function () {
    const ctx = {}
    return Promise.resolve(fn1(ctx, () => {
      return Promise.resolve(fn2(ctx, () => {
      }))
    }))
  }
}
mycompose()()
複製代碼

總結

Koa 相對於 Express 來講輕量了好多,它只是一個基礎的結構,當須要到其餘功能時,再使用相應的中間件,上文所講到的 koa-compose 就是其中之一,其路由及其餘功能又會在其餘中間件實現,因此你能夠看到,即便是 koa-compose 所有的代碼依然很簡練。post

相關文章
相關標籤/搜索