server.listen(PORT); console.log(</span>server started at http://localhost:<span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">PORT</span><span class="pl-pse">}</span></span><span class="pl-pds">);
use(fn) {
if (typeof fn !=='function') thrownewTypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. '+'See the documentation for examples of how to convert old middleware '+'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn =convert(fn);
}
debug('use %s', fn._name||fn.name||'-');
this.middleware.push(fn);
returnthis;
}
// Middleware normally takes two parameters (ctx, next), ctx is the context for one request,// next is a function that is invoked to execute the downstream middleware. It returns a Promise with a then function for running code after completion.
functioncompose(middleware) {
if (!Array.isArray(middleware))
thrownewTypeError("Middleware stack must be an array!");
for (constfnof middleware) {
if (typeof fn !=="function")
thrownewTypeError("Middleware must be composed of functions!");
}
returnfunction(context, next) { // last called middleware # let index =-1; returndispatch(0); functiondispatch(i) { if (i <= index) returnPromise.reject(newError("next() called multiple times"));
index = i; let fn = middleware[i]; if (i ===middleware.length) fn = next; if (!fn) returnPromise.resolve(); try { returnPromise.resolve(fn(context, dispatch.bind(null, i +1)));
} catch (err) { returnPromise.reject(err);
}
}
};
}
这个方法只做了两件事,
定义了一个 dispatch 方法,
然后调用它 dispatch(0)
这里中间件从数组中取出并顺次执行的逻辑便在 dispatch 函数中。
整体方法体中维护了一个索引 index 其初始值为 -1,后面每调用一次 dispatch 会加 1。当执行 dispatch(0) 时,从中间件数组 middleware 中取出第 0 个中间件并执行,同时将 dispatch(i+1) 作为 next 传递到下一次执行。
let fn = middleware[i];
returnPromise.resolve(fn(context, dispatch.bind(null, i +1)));
所以这里就能理解,为什么中间件中必需调用 next,否则后续中间件不会执行。
这样一直进行下去直到所有中间件执行完毕,此时 i === middleware.length,最后一个中间件已经执行完毕,next 是没有值的,所以直接 resolve 掉结束中间件执行流程。
if (i ===middleware.length) fn = next;
if (!fn) returnPromise.resolve();