compose是koa的核心組件,負(fù)責(zé)中間件注冊后的調(diào)用筷转,可以實現(xiàn)多函數(shù)的可控鏈?zhǔn)秸{(diào)用。compose的使用如下:
function A(context, next) {
console.log('A');
next();
}
function B(context, next) {
console.log('B');
}
function C(context, next) {
console.log('C');
next();
}
const handler = compose([A, B, C]);
handler(null);
console:
A
B
上述示例中C不會觸發(fā)因為函數(shù)B中未調(diào)用next()凭戴。
看了以上的代碼箕速,很多人使用koa框架時酪碘,對中間件函數(shù)通過next()觸發(fā)下一個中間件的原理不甚了解。接下來會逐步還原compose的實現(xiàn)盐茎,同時對源碼實現(xiàn)進(jìn)行解讀兴垦。
function compose(funcs) {
return function(context) {
return dispatch(0);
function dispatch(i) {
return funcs[i](context, dispatch.bind(null, i+1))
}
}
}
以上簡單實現(xiàn)了compose函數(shù)字柠,當(dāng)然compose函數(shù)官方實現(xiàn)比這個復(fù)雜探越,但這段代碼可以看出compose函數(shù)的核心原理,compose函數(shù)的核心是建立dispatch函數(shù)和funcs函數(shù)數(shù)組的聯(lián)系募谎。
dispatch調(diào)用流程
compose函數(shù)運行流程如以上所示扶关,我們可以發(fā)現(xiàn)執(zhí)行compose(funcs)(ctx)等價于執(zhí)行disptch(0),執(zhí)行dispatch(i)等價于執(zhí)行funcs[i](ctx, dispatch(null, i+1))数冬。 意不意外节槐,傳給中間件函數(shù)的next參數(shù)就是dispatch.bind(null, i+1),執(zhí)行這個next函數(shù)就會觸發(fā)下一個中間件函數(shù)拐纱。
現(xiàn)在考慮compose函數(shù)的邊際條件铜异,來完成官方實現(xiàn)。
- 首先校驗compose函數(shù)的輸入秸架,其輸入必須是一個函數(shù)列表揍庄;
- 限制每個中間件函數(shù)中next()的調(diào)用次數(shù),最多只能調(diào)用一次东抹;
- 對dispatch的參數(shù)進(jìn)行校驗蚂子,i不能大于函數(shù)列表的長度,即i < funcs.length缭黔;
接下來實現(xiàn)上述邊際條件:
function compose(funcs) {
if (!Array.isArray(funcs)) {
throw new Error('param funcs must be an array');
}
for (let i = 0; i < funcs.length; i++) {
if (typeof funcs[i] !== 'function') {
throw new Error('param funcs must be an array of functions')
}
}
return function(context) {
let index = -1;
if (index === i) {
throw new Error('next function triggered more than once');
}
return dispatch(0);
function dispatch(i) {
index++;
return i < funcs.length ? funcs[i](context, dispatch.bind(null, i+1)) : null;
}
}
}