您现在的位置是:网站首页 > 异步中间件的实现方式文章详情

异步中间件的实现方式

在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
]);

错误处理策略

异步错误需要特殊处理机制:

  1. 集中式错误处理器:
app.use((err, req, res, next) => {
  if (err instanceof DatabaseError) {
    return res.status(503).send('Service Unavailable');
  }
  next(err);
});
  1. 包装异步函数:
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);

性能优化技巧

异步操作可能成为性能瓶颈:

  1. 缓存中间件结果:
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();
});
  1. 流式处理大文件:
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');
// 现在异步错误将显示完整调用链

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步