您现在的位置是:网站首页 > 异步中间件的实现方式文章详情
异步中间件的实现方式
陈川
【
Node.js
】
31410人已围观
4283字
在Express框架中,异步中间件的实现方式对于处理复杂的异步逻辑至关重要。通过合理的异步控制,可以避免回调地狱并提升代码可维护性。
回调函数与错误优先模式
传统的Express中间件通过回调函数处理异步操作。错误优先(Error-first)是Node.js的核心模式,中间件中需遵循这一规范:
app.get('/data', (req, res, next) => {
fs.readFile('data.json', (err, data) => {
if (err) return next(err); // 错误优先传递
req.fileData = JSON.parse(data);
next();
});
});
典型问题出现在嵌套回调中:
// 回调地狱示例
app.get('/user', (req, res, next) => {
User.findById(req.params.id, (err, user) => {
if (err) return next(err);
Profile.get(user.profileId, (err, profile) => {
if (err) return next(err);
Analytics.fetch(user.id, (err, stats) => {
// 嵌套层级持续加深...
});
});
});
});
Promise链式调用
使用Promise可显著改善异步代码结构。Express 5开始原生支持返回Promise的中间件:
app.get('/products', async (req, res, next) => {
try {
const products = await ProductModel.find().exec();
const inventory = await InventoryService.check(products.map(p => p.id));
res.json({ products, inventory });
} catch (err) {
next(err); // 自动捕获异步错误
}
});
重要特性包括:
- 使用
async/await
时无需显式调用next()
- 未捕获的Promise拒绝会被自动转发到错误处理中间件
- 支持中间件返回Promise对象
异步控制流模式
复杂业务场景需要组合多个异步操作:
并行执行:
app.get('/dashboard', async (req, res) => {
const [user, notifications, messages] = await Promise.all([
User.fetch(req.user.id),
Notification.count({ userId: req.user.id }),
Message.findUnread(req.user.id)
]);
res.render('dashboard', { user, notifications, messages });
});
顺序依赖:
app.post('/order', [
validateOrder, // 同步校验
async (req, res) => {
const payment = await processPayment(req.body);
req.paymentId = payment.id;
},
createOrder, // 依赖payment结果的中间件
sendConfirmation
]);
错误处理策略
异步错误需要特殊处理机制:
- 集中式错误处理器:
app.use((err, req, res, next) => {
if (err instanceof DatabaseError) {
return res.status(503).send('Service Unavailable');
}
next(err);
});
- 包装异步函数:
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/api/data', asyncHandler(async (req, res) => {
const data = await riskyOperation();
res.json(data);
}));
高级组合模式
对于企业级应用,可考虑以下模式:
中间件工厂函数:
function requireRole(role) {
return async (req, res, next) => {
const hasRole = await checkUserRole(req.user, role);
if (!hasRole) return next(new ForbiddenError());
next();
};
}
app.delete('/posts/:id', [
auth.required,
requireRole('admin'),
async (req, res) => {
await Post.deleteOne({ _id: req.params.id });
res.sendStatus(204);
}
]);
可配置的中间件链:
const pipeline = [
middlewareA,
condition ? middlewareB : middlewareC,
...(features.analytics ? [trackingMiddleware] : [])
];
app.use('/special-route', pipeline);
性能优化技巧
异步操作可能成为性能瓶颈:
- 缓存中间件结果:
const userCache = new Map();
app.use(async (req, res, next) => {
if (!userCache.has(req.token)) {
userCache.set(req.token, await fetchUser(req.token));
}
req.user = userCache.get(req.token);
next();
});
- 流式处理大文件:
app.post('/upload', (req, res) => {
const processor = new Transform({
transform(chunk, encoding, callback) {
// 流式处理数据
callback(null, transformData(chunk));
}
});
req.pipe(processor).pipe(res);
});
测试异步中间件
使用Jest等框架测试时需注意:
describe('auth middleware', () => {
const mockRequest = (headers) => ({ headers });
const mockResponse = () => ({
status: jest.fn().mockReturnThis(),
json: jest.fn()
});
test('rejects missing token', async () => {
const req = mockRequest();
const res = mockResponse();
const next = jest.fn();
await authMiddleware(req, res, next);
expect(res.status).toHaveBeenCalledWith(401);
});
});
与现代前端集成
处理GraphQL等现代API的中间件示例:
app.use('/graphql', express.json(), async (req, res) => {
const context = await buildContext(req);
const result = await execute({
schema,
document: parse(req.body.query),
contextValue: context
});
res.json(result);
});
调试异步堆栈
通过longjohn
等工具增强异步堆栈追踪:
npm install longjohn
require('longjohn');
// 现在异步错误将显示完整调用链
上一篇: 错误处理中间件的特殊用法
下一篇: 中间件的复用与模块化