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) {
// 一个中间件里多次调用next
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
// fn就是当前的中间件
let fn = middleware[i]
if (i === middleware.length) fn = next // 最后一个中间件如果也next时进入(一般最后一个中间件是直接操作ctx.body,并不需要next了)
if (!fn) return Promise.resolve() // 没有中间件,直接返回成功
try {
/*
* 使用了bind函数返回新的函数,类似下面的代码
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
*/
// dispatch.bind(null, i + 1)就是中间件里的next参数,调用它就可以进入下一个中间件
// fn如果返回的是Promise对象,Promise.resolve直接把这个对象返回
// fn如果返回的是普通对象,Promise.resovle把它Promise化
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
// 中间件是async的函数,报错不会走这里,直接在fnMiddleware的catch中捕获
// 捕获中间件是普通函数时的报错,Promise化,这样才能走到fnMiddleware的catch方法
return Promise.reject(err)
}
}
}
}
const context = {};
const sleep = (time) => new Promise(resolve => setTimeout(resolve, time));
const test1 = async (context, next) => {
console.log('1-start');
context.age = 11;
await next();
console.log('1-end');
};
const test2 = async (context, next) => {
console.log('2-start');
context.name = 'deepred';
await sleep(2000);
console.log('2-end');
};
const fn = compose([test1, test2]);
fn(context).then(() => {
console.log('end');
console.log(context);
});