koa-compose

熟悉 koa-compose 中间件源码

歌德曾说:读一本好书,就是在和高尚的人谈话。 同理可得:读源码,也算是和作者的一种学习交流的方式。

参考

juejin.cn/post/700537…

koa-compose

中间件是指连贯整个 Koa 应用程序,并共享资源的独立插件,“连贯”对应“next”,“共享资源对应context”。

koa的文档上有个非常代表性的中间件 gif

compose

处理中间件流程主要是用compose函数

compose执行逻辑:

  • 接收一个参数,校验参数是数组,且校验数组中的每一项是函数。
  • 返回一个函数,这个函数接收两个参数,分别是contextnext,这个函数最后返回Promise
/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */
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){
      // 省略,下文讲述
    }
  }
}

复制代码

dispatch

function dispatch (i) {
  // 取出数组里的 fn1, fn2, fn3...
  let fn = middleware[i]
 //执行compose(stack)({})时,执行中间件数组里的第一个fn,执行过程中执行到next(),也就是执行了dispatch.bind(null, i + 1),会去执行中间件数组里的下一个fn,以此类推
   return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
}

复制代码
  • 完整
# 三个细节
1.fn里多次调用next函数,这里用闭包index保存计数很牛逼
2.最后一个fn无next,返回Promise.resolve()
3.错误捕获由上一个fn来包裹执行
function dispatch (i) {
  // 一个函数中多次调用报错
  // await next() 这里的index是闭包数据,每一次掉用dispatch都会更新,若有一次fn执行了两次next,更新会有冲突
  // await next()
  if (i <= index) return Promise.reject(new Error('next() called multiple times'))
  index = i
  // 取出数组里的 fn1, fn2, fn3...
  let fn = middleware[i]
  // 最后 相等,next 为 undefined
  if (i === middleware.length) fn = next
  // 直接返回 Promise.resolve()
  if (!fn) return Promise.resolve()
    //fn发生错误时,不执行上一个fn的next后面的代码,由上一个fn的catch函数执行相应逻辑
  try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
  } catch (err) {
    return Promise.reject(err)
  }
}
复制代码

小记

1.bind函数中,如果函数不需要使用this,一般会写成null

2.闭包可以用来计数,排查重复执行

function compose(){
	let index = -1
	dispatch(0)
	dispatch(i){
	// dispatch(0) 0《-1 
	// dispatch(1) 1《 0 	1《1 	error
		if(i<=index) throw ..
		index = i
		return fn(dispatch(i+1))
	}
}
fn(next){
	next()
	next()
}
复制代码

3.川哥yyds

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享