express
先来一段 express 代码
// app.js
var express = require'express');
var path = require'path');
var app = express);
// view engine setup
app.set'views', path.join__dirname, 'views'));
app.set'view engine', 'pug');
app.useexpress.staticpath.join__dirname, 'public')));
app.use'/test', functionreq, res, next) {
console.logreq.headers);
console.logreq.body);
console.logreq.query);
res.header{
'Content-type': 'application/json'
});
res.json{
success: true
});
});
module.exports = app;
其 app.use 就只是把回调函数放进栈里,用 Layer 包裹,Layer 结构
function Layerpath, options, fn) {
if !this instanceof Layer)) {
return new Layerpath, options, fn);
}
debug'new %o', path)
var opts = options || {};
// 处理的回调函数放这里
this.handle = fn;
this.name = fn.name || '<anonymous>';
this.params = undefined;
this.path = undefined;
this.regexp = pathRegexppath, this.keys = [], opts);
// set fast path flags
this.regexp.fast_star = path === '*'
this.regexp.fast_slash = path === '/' && opts.end === false
}
调用时执行中间件的 handle,中间件里再手动执行 next 来链式执行下去,删减边界判断的逻辑,大体如下
// handle 函数里
proto.handle = function handlereq, res, out) {
var idx = 0
var stack = self.stack;
next);
function nexterr) {
var layerError = err === 'route'
? null
: err;
// no more matching layers
// 函数出口。最后一个调用next,就从这里执行done出去
if idx >= stack.length) {
setImmediatedone, layerError);
return;
}
// find next matching layer
var layer = stack[idx++];
var match;
var route = layer.route;
var fn = layer.handle;
fnreq, res, next);
}
}
总结一下,app.use 就是往中间件数组中塞入新的中间件。中间件的执行则依靠私有方法 app.handle进行处理,按顺序寻找中间件,不断的调用 next 往下执行下一个中间件。
koa
koa 是由 compose 函数执行中间件,其实现抽离成了一个单独的包,代码简单。
function compose middleware) {
// ...
return function context, next) {
// last called middleware #
let index = -1
return dispatch0)
// 递归执行所有的 middleware,返回 Promise 对象。
function dispatch i) {
if i <= index) return Promise.rejectnew Error'next) called multiple times'))
index = i
let fn = middleware[i]
// 如果中间件 next 前的代码已经执行完了
// 接下来 fn 就被赋值为 next 后面的代码。
if i === middleware.length) fn = next
if !fn) return Promise.resolve)
try {
// 执行下一个中间件 next 前的代码。
return Promise.resolvefncontext, dispatch.bindnull, i + 1)));
} catch err) {
return Promise.rejecterr)
}
}
}
}
但比较上文 express 最大的不同并非 async/await 实现了更强大的功能。那就是 next 后还可以执行代码
const Koa = require'koa');
const app = new Koa);
app.useasync ctx, next) => {
console.log'test1')
next)
console.log'test3')
});
app.useasync ctx, next) => {
console.log'test2')
next)
console.log'test4')
});
// response
app.useasync ctx => {
ctx.body = 'Hello World';
});
// 访问localhost:3000
// 打印 test1 -> test2 -> test3 -> test4
app.listen3000);
这就是所谓的 Koa 洋葱圈模型。