koa-compose 是koa 框架的根源的根源 ,是其實現洋蔥包裹型中間件的基礎數組
如下是koa2.X 版本因此依賴的compose 版本 ,其主要核心依賴於new Promise.resolve();遍歷 middleware 中間件集合,經過遞歸的方式來讓每一個prmise按步執行promise
注:koa每添加一箇中間件實則相 ,給koa對象的middleware 中間件數組push一個新值;緩存
//koa 對象的use方法最核心代碼use(fn) { this.middleware.push(fn); return this; }
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!') } return function (context, next) { //設置初始下標 let index = -1 //直接執行dispatch,而且從中間件數組下標0處開始執行中間件; return dispatch(0); /** * dispatch 函數,屬於內部核心模塊 */ function dispatch (i) { //合理性判斷,若是若是參數小於等於下標就說明當前中間件被屢次執行則拋出異常 if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i // 對已參數爲下標的當前中間件進行局部緩存 let fn = middleware[i] // 判斷是否已到達中間件數組末尾,若是到達末尾則 返回不帶參數的promise if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { //正常狀況函數執行最終返回帶有回調的promise,而且其參數就爲某一箇中間件 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),那麼就進入了第二個中間件;koa
第二次,此時第二個中間件被調用,dispatch(1),展開:異步
Promise.resolve(function(context, 中間件2){ //中間件一第一部分代碼 await/yield Promise.resolve(function(context, next){ //中間件二第一部分代碼 await/yield next(); //中間件二第二部分代碼 }()) //中間件一第二部分代碼 }());
很明顯這裏的next指向dispatch(2),那麼就進入了第三個中間件;函數
第三次,此時第二個中間件被調用,dispatch(2),展開:this
Promise.resolve(function(context, 中間件2){ //中間件一第一部分代碼 await/yield Promise.resolve(function(context, 中間件3){ //中間件二第一部分代碼 await/yield Promise(function(context){ //中間件三代碼 }()); //中間件二第二部分代碼 }) //中間件一第二部分代碼 }());