koa-compose 是koa 框架的根源的根源 ,是其实现洋葱包裹型中间件的基础
以下是koa2.X 版本所以依赖的compose 版本 ,其主要核心依赖于new Promise.resolve();遍历 middleware 中间件集合,通过递归的方式来让每个prmise按步执行
注:koa每添加一个中间件实则相 ,给koa对象的middleware 中间件数组push一个新值;
//koa 对象的use方法最核心代码use(fn) { this.middleware.push(fn); return this; }
compose核心代码 【compose其实就是一个递归函数】
Compose 是一种基于 Promise 的流程控制方式,可以通过这种方式对异步流程同步化,解决之前的嵌套回调和 Promise 链式耦合。
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, function next () { return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } } }
第一次,此时第一个中间件被调用,dispatch(0),展开:
Promise.resolve(function(context, next){ //中间件一第一部分代码 await/yield next(); //中间件一第二部分代码 }());
很明显这里的next指向dispatch(1),那么就进入了第二个中间件;
第二次,此时第二个中间件被调用,dispatch(1),展开:
Promise.resolve(function(context, 中间件2){ //中间件一第一部分代码 await/yield Promise.resolve(function(context, next){ //中间件二第一部分代码 await/yield next(); //中间件二第二部分代码 }()) //中间件一第二部分代码 }());
很明显这里的next指向dispatch(2),那么就进入了第三个中间件;
第三次,此时第二个中间件被调用,dispatch(2),展开:
Promise.resolve(function(context, 中间件2){ //中间件一第一部分代码 await/yield Promise.resolve(function(context, 中间件3){ //中间件二第一部分代码 await/yield Promise(function(context){ //中间件三代码 }()); //中间件二第二部分代码 }) //中间件一第二部分代码 }());