You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express 5中Promise未触发全局错误处理的问题咨询

问题背景

我编写了如下控制器代码:

async signUp(req: Request, res: Response): Promise<UserDto> {
    console.log('controller');
    const createUserDto = plainToInstance(CreateUserDto, req.body);
    const newUserDto = new UserDto(
      await this.userService.create(createUserDto),
    );
    res.status(201).json({
      message: 'User created',
      user: plainToInstance(UserDto, newUserDto, {
        excludeExtraneousValues: true,
      }),
    });
    return new UserDto(newUserDto);
  }

已知从Express 5开始,返回Promise的路由处理器和中间件在拒绝或抛出错误时会自动调用next(value),我安装的Express版本为^5.1.0。

我的路由定义如下:

userRoutes.post(
  '/users/signup',
  validateBody(CreateUserDto),
  async (req, res) => {
    userController.signUp(req, res);
  },
);

当我在控制器中抛出错误时:

async signUp(req: Request, res: Response): Promise<UserDto> {
    console.log('controller');
    throw new Error('some error');
    const createUserDto = plainToInstance(CreateUserDto, req.body);
    const newUserDto = new UserDto(
      await this.userService.create(createUserDto),
    );
    res.status(201).json({
      message: 'User created',
      user: plainToInstance(UserDto, newUserDto, {
        excludeExtraneousValues: true,
      }),
    });
    return new UserDto(newUserDto);
  }

服务器崩溃且错误未被全局错误处理器捕获。我的全局错误处理器代码如下:

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction,
) => {
  console.error(err);
  res.status(500).send({ errors: [{ message: 'Something went wrong' }] });
};

并已将其添加到应用中:

const app = express();

app.use(express.json());
app.use('/api', userRoutes);

app.use(errorHandler);

当我修改路由定义为以下形式时,错误能被全局错误处理器捕获:

userRoutes.post(
  '/users/signup',
  validateBody(CreateUserDto),
  async (req, res) => {
    await userController.signUp(req, res);
  },
);

我有两个问题:

  1. 为何最初的实现不符合预期?
  2. 路由定义中是否需要async关键字?

解答

1. 最初实现不符合预期的原因

你在路由处理器里调用userController.signUp(req, res)时没有加await,导致:

  • signUp是异步函数,调用后会立即返回一个Promise对象,但这个Promise没有被任何代码处理。
  • 当signUp内部抛出错误时,Promise会变成拒绝状态,但因为没有被await或.catch()处理,这就成了未处理的Promise拒绝。Express只能捕获路由处理器本身返回的Promise的拒绝,无法感知到这个游离的拒绝Promise。
  • 未处理的Promise拒绝在Node.js环境中默认会触发进程崩溃,所以服务器直接崩溃,错误根本没机会走到全局错误处理器。

加上await后,路由处理器的异步函数会等待signUp的Promise完成,一旦Promise拒绝,路由处理器本身的Promise也会跟着拒绝,Express 5会自动把这个错误传递给next(),最终被全局错误处理器捕获。

2. 路由定义中是否需要async关键字?

分场景判断:

  • 如果路由处理器内部要使用await(比如等待控制器的异步方法),必须加async关键字,否则await会直接报错。
  • 即使你不直接用await,但如果路由处理器要返回Promise(比如直接返回控制器的异步方法调用结果),也需要加async,或者直接把控制器方法作为路由处理器(注意绑定this上下文):
    userRoutes.post(
      '/users/signup',
      validateBody(CreateUserDto),
      userController.signUp.bind(userController)
    );
    
    这种情况下,Express 5会直接处理signUp返回的Promise,错误也能被正常捕获。

如果路由处理器里没有异步操作,也不返回Promise,那可以不用async,但你的场景中控制器方法是异步的,所以必须用async配合await,或者直接绑定控制器方法作为路由处理器。


内容的提问来源于stack exchange,提问作者derstauner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 11:27:36