# koa-compose

# 环境准备

git clone https://github.com/koajs/compose.git

# package.json

  "files": [
    "index.js"
  ],
  "dependencies": {},
  "devDependencies": {
    "codecov": "^3.0.0",
    "jest": "^27.0.6",
    "standard": "^16.0.3"
  },
  "scripts": {
"eslint": "standard --fix .",
    "test": "jest"
  },
  • jest,一个比较流行的测试库,文档: https://jestjs.io/
  • standard,一种js代码规范的库

# index.js

因为这一次源码并不长,就直接在源码上编写注释进行学习了。

'use strict'

/**
 * Expose compositor.
 */

module.exports = compose

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose (middleware) {
  // 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!')
  }

  /**
   * @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]
      // length 为 next 函数
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        // 以 下一个中间件的调用结果 作为resolve的值进行传递
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

# 参考资料

  • 若川的文章 https://github.com/lxchuan12/koa-compose-analysis 掘金链接:50行代码串行Promise,koa洋葱模型原来是这么实现?
  • koa-compose 涉及到的设计模式叫职责链模式
  • 可以翻阅《JavaScript设计模式与开发实践》第十三章