KOA技术分享

专注 Koa.js 框架的编程知识分享

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 应用。

← 下一篇:RESTful API 进阶实践与性能优化 上篇:Koa.js服务端渲染框架与首屏优化 →