koa-compose解析
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
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
compose 函数的返回值仍然是一个函数,且接受两个参数。
let func = compose([Function])
func(context, next)
调用 func 函数,会执行 func 的内部函数 dispatch,并将 dispatch 的返回值作为 func 的返回值
return dispatch(0)
首先执行数组中的第一个函数,数组中的函数也接受两个参数,其中第二个参数仍然为 dispatch 函数,并且其参数值为数组中下一个函数的索引值。假设数组中的函数为 f1...fn,则:
function f1 (ctx, next) {
console.log(1)
await next()
console.log(6)
}
function f2 (ctx, next) {
console.log(2)
await next()
console.log(5)
}
function fn (ctx, next) {
console.log(3)
await next()
console.log(4)
}
数组中的每一个函数都去调用 dispatch ,就可保证整个数组中的函数都会被顺序调用。
如果某个函数没有调用 dispatch 那么这个函数就是数组中最后一个会被调用的函数。
如果数组中最后一个函数也调用了 dispatch 那么,因为数组中已没有下一个函数,此时会把下一个函数设置为 func 的第二个参数,此参数不存在则顺序调用结束,开始反向执行。
通常数组中最后一个函数就不再调用 dispatch
完整代码
'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) {
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) {
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()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}