题目
实现一个中间件执行流程的洋葱模型
信息
- 类型:问答
- 难度:⭐⭐
考点
中间件原理, 洋葱模型, 异步流程控制
快速回答
实现洋葱模型的核心要点:
- 使用
compose函数组合中间件 - 每个中间件接收
context和next参数 - 通过
await next()控制执行流程 - 递归执行形成先进后出的调用栈
原理说明
洋葱模型指请求从外到内穿透中间件层,响应从内到外返回的流程。核心是compose函数:
- 中间件数组被组合成嵌套函数
- 每个中间件执行时遇到
await next()暂停 - 控制权转移到下一个中间件
- 当最后一个中间件完成后,逐层回溯执行剩余代码
执行顺序:
Middleware1 start → Middleware2 start → Middleware3 start → 业务逻辑 → Middleware3 end → Middleware2 end → Middleware1 end
代码实现
function compose(middlewares) {
return function (context) {
return dispatch(0);
function dispatch(i) {
const fn = middlewares[i];
if (!fn) return Promise.resolve();
try {
// 关键:next 是下一个中间件的封装
return fn(context, () => dispatch(i + 1));
} catch (err) {
return Promise.reject(err);
}
}
};
}
// 使用示例
const app = {
middlewares: [],
use(fn) {
this.middlewares.push(fn);
},
run(ctx) {
return compose(this.middlewares)(ctx);
}
};
// 添加中间件
app.use(async (ctx, next) => {
console.log('Middleware 1 start');
await next(); // 执行权移交
console.log('Middleware 1 end');
});
app.use(async (ctx, next) => {
console.log('Middleware 2 start');
ctx.data = 'processed';
await next();
console.log('Middleware 2 end');
});
// 运行
app.run({}).then(() => console.log('Done'));
/* 输出:
Middleware 1 start
Middleware 2 start
Middleware 2 end
Middleware 1 end
Done
*/
最佳实践
- 错误处理:在最外层添加错误捕获中间件
- 性能优化:避免在中间件中进行阻塞操作
- 上下文设计:使用独立
context对象避免污染全局 - 异步控制:始终返回Promise保证执行顺序
常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
忘记调用await next() | 后续中间件不执行 | 确保每个中间件正确调用next |
| 未处理异步错误 | 导致进程崩溃 | 用try/catch包裹中间件逻辑 |
| 修改共享状态未同步 | 上下文数据不一致 | 使用async/await保证执行顺序 |
扩展知识
- Koa vs Express:Koa使用洋葱模型,Express是线性流水线
- 性能影响:中间件层级不宜过深(建议≤10层)
- 适用场景:身份验证、日志记录、请求超时处理等
- 高级技巧:使用
Promise.all实现并行中间件执行