Koa.js异步编程深入实践与性能优化
async/await异常处理最佳实践
在 Koa.js 开发中,异步编程是核心。async/await 虽然简化了异步代码,但异常处理不当会导致未捕获的错误使整个应用崩溃。本文探讨 async/await 的异常处理策略。
try-catch 捕获异常
最基本的异常处理方式,使用 try-catch 包裹异步代码:
// 基础try-catch写法
router.get('/user/:id', async (ctx) => {
try {
const user = await getUserById(ctx.params.id);
if (!user) {
ctx.status = 404;
ctx.body = { error: '用户不存在' };
return;
}
ctx.body = user;
} catch (err) {
ctx.status = 500;
ctx.body = { error: '服务器错误', message: err.message };
}
});
封装统一错误处理中间件
为了避免在每个路由中重复 try-catch,可以封装统一的错误处理中间件:
// 错误捕获中间件
const errorHandler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || err.statusCode || 500;
ctx.body = {
error: err.message,
code: err.code
};
ctx.app.emit('error', err, ctx);
}
};
app.use(errorHandler);
Promise并发控制
在实际应用中,经常需要并发执行多个异步操作,如何控制并发数量和错误处理非常重要。
Promise.all 并发执行
// 并发获取用户信息和订单列表
router.get('/user-dashboard', async (ctx) => {
const userId = ctx.session.userId;
// 并发执行两个查询
const [user, orders] = await Promise.all([
getUserById(userId),
getOrdersByUserId(userId)
]);
ctx.body = { user, orders };
});
控制并发数量的 Promise.map
当需要处理大量并发任务时,需要控制并发数量避免资源耗尽:
const pLimit = require('p-limit');
// 限制并发数为3
const limit = pLimit(3);
const tasks = dataList.map(item => {
return limit(() => processItem(item));
});
const results = await Promise.all(tasks);
Promise.allSettled 处理部分失败
当希望部分成功部分失败时,使用 Promise.allSettled:
const results = await Promise.allSettled([
fetchUser(),
fetchOrders(),
fetchNotifications()
]);
// 处理每个结果
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`任务${index}成功:`, result.value);
} else {
console.log(`任务${index}失败:`, result.reason);
}
});
async 钩子函数
Koa v2.7+ 支持 async 钩子函数,可以更方便地处理异步任务:
// 使用 async beforeStart 钩子
app.beforeStart(async () => {
// 确保数据库连接后再启动
await database.connect();
console.log('数据库连接成功');
});
// 使用 async server 钩子
app.on('error', async (err, ctx) => {
// 异步记录错误日志
await Logger.error({
message: err.message,
stack: err.stack,
url: ctx.url,
method: ctx.method
});
});
性能优化技巧
1. 避免不必要的 await
对于没有依赖关系的异步任务,并发执行而不是串行 await:
// 错误写法 - 串行执行 const user = await getUser(id); const orders = await getOrders(id); // 正确写法 - 并行执行 const [user, orders] = await Promise.all([ getUser(id), getOrders(id) ]);
2. 使用 for...of 串行处理需要顺序的任务
// 需要按顺序执行的任务
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
3. 合理使用缓存避免重复请求
const cache = new Map();
// 使用缓存避免重复请求
async function getData(key) {
if (cache.has(key)) {
return cache.get(key);
}
const data = await fetchData(key);
cache.set(key, data);
// 设置过期时间
setTimeout(() => cache.delete(key), 5 * 60 * 1000);
return data;
}
总结
Koa.js 的异步编程需要特别注意异常处理和并发控制。合理使用 async/await、 Promise.all、Promise.allSettled 等特性,配合适当的错误处理中间件,可以构建稳定高效的 Node.js 应用。