Koa原始碼解析

yjy5264發表於2019-02-16

用法回顧

const Koa = require(`koa`);
const app = new Koa();

app.use(async (ctx, next) => {
  fn1();
  await next();
  fn2();
});

app.use(async (ctx, next) => {
  fn3();
  await next();
  fn4()
});

app.use(async (ctx, next) => {
  fn5();
  await next();
  fn6()
});

app.listen(3000);

執行順序 fn1 -> fn3 -> fn5 -> fn6 -> fn4 -> fn2
每當執行next時,執行下一個中介軟體,執行到最後一箇中介軟體後開始往回執行

原始碼解析

原始碼執行步驟

  1. 使用use方法即將middleware push 進koa中的this.middleware陣列中
  2. listen方法呼叫node的http.createServer和server.listen方法來建立服務,createServer的回掉執行下面的操作
  3. 回掉首先執行compose(this.middleware)方法將middleware組合成一個promise物件來執行,這個promise物件即可完成中介軟體級聯的操作
  4. 將回掉傳出的值req和res用於建立一個context物件,裡面包含了request物件和response物件,這些物件提供了許多後臺開發需要的引數和方法
  5. 執行中介軟體
  6. 得到結果給respond方法格式化資料
  7. 或者捕獲異常給onerror方法處理異常

compose

koa原始碼最重要的部分,如何實現中介軟體級聯,以下是compose方法的原始碼(為方便觀看,部分進行了ES6處理)

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!`)
  }

  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 // next = undefined
      if (!fn) return Promise.resolve() // 當執行到最後一個middleware時結束
      try {
        return Promise.resolve(
          fn(context, next = () => dispatch(i + 1))
        )
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
  1. compose方法使用遞迴的方式遍歷每一個middleware
  2. 遍歷時將下一個middleware當作next傳遞給當前的middleware

更多文章 yjy5264.github.io


相關文章