koa-compose 的源码分析如下:
1 | //@koa-compose v4.1.0 |
compose 方法的参数为 middleware 数组, 使用 app.use(fn) 时,中间件会被添加到该数组中。
compose 首选会对 middleware 的类型和其保存的每一个中间件的类型进行判断,如果有任一不符合,则抛出错误。
1 | if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') |
然后返回一个匿名函数。匿名函数接受两个参数 context ,next, context 为 koa 实例的上下文对象,由 koa\lib\context.js 定义。
匿名函数定义了一个初始值为 -1 的 index 变量,并通过对内部函数 dispatch 的调用,实现 对 index 变量和 dispatch 的闭包。
dispatch 通过 i 变量判断目前是第几个中间件的调用(以 0 开始), 通过 index 变量防止在当前中间件中多次调用 next()
如果 middleware 为空的话直接返回 Promise.resolve(), 否则调用取出的中间件函数并为其传递 context 和 dispatch.bind(null, i + 1) 函数, 此时就进入了第一个中间件函数的执行。
1 | app.use(function(ctx, next) { |
中间件函数的第一个函数为 context, 第二个参数为 dispatch.bind(null, i + 1) 函数,当执行到 next() 时,就进入到了被 bind i + 1 的 dispatch 中, 重复 dispatch 中的逻辑,取出下一个中间件执行,直到 i=midderware.length, 此时取出的值为 undefined, 导致 if (!fn) return Promise.resolve() 成立,这时就返回到上一个中间函数的以 next() 为分割点的后部分代码往下执行,直至返回到最开始执行的 dispatch(0) 函数被 resolve 或 reject. 这样的执行方式就像洋葱的纹理一样。