跳到主要内容

实现

2023年08月03日
柏拉文
越努力,越幸运

一、认识


express 中间件用于处理 http 的请求和响应。采用 尾递归 的方式, 中间件一个接一个的顺序执行, 习惯于将 response 响应写在最后一个中间件中。

二、实现


function compose(req,res,middleware) {
let index = 0;

function next(error) {
if (index >= middleware.length) {
console.log("error", error);
return;
}
while (index < middleware.length) {
const fn = middleware[index++];
fn(req, res, next);
}
}

next();
}

next 不断取出 middleware 中的中间件进行调用, 同时把 next 本身传递给 中间件 作为第三个参数。每个中间件约定的固定形式为 (req, res, next) => {}, 这样每个**中间件*函数中只要调用 next 方法即可传递调用下一个中间件。

express 的中间件是一个 尾递归调用, 之所以说是尾递归是因为递归函数的最后一条语句是调用函数本身,所以每一个中间件的最后一条语句需要是**next()才能形成尾递归**,否则就是普通递归,尾递归相对于普通”递归“的好处在于节省内存空间,不会形成深度嵌套的函数调用栈。

三、调试


let fn1 = async function (context, next) {
console.log("fn1 之前");
await next();
console.log("fn1 之后");
};
let fn2 = async function (context, next) {
console.log("fn2 之前");
await next();
console.log("fn2 之后");
};
let fn3 = async function (context, next) {
console.log("fn3 之前");
await next(); // 最后一个中间件的 next 可有可无
console.log("fn3 之后");
};

let middleware = [fn1, fn2, fn3];
compose(middleware)({});

四、问题


4.1 express 可以用 async function 作为中间件用于异步处理吗?

答: express 的中间件的执行是同步的 while 循环, 当中间件同时包含普通普通函数和 async 函数时, 执行顺序会打乱。有可能执行到 await 后再次执行 next 索引会超出的情况。

参考资料


浅谈redux, koa, express 中间件实现对比解析