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

NestJS循环调用服务抛出NotFoundException异常未按预期工作

问题描述

我有一个findOne exercice服务,当训练项ID不存在时会抛出NotFoundException异常,代码如下:

async findOne(id: string | Exercice) {
  if (!isValidObjectId(id)) {
    throw new BadRequestException('ID is not valid');
  }

  const exercice = await this.exerciceModel
    .findById(id)
    .populate('bodyPart targetMuscle')
    .select('-__v');

  if (!exercice) {
    throw new NotFoundException('exercice not found');
  }

  return exercice;
}

在创建训练计划的服务中,我需要验证请求体里的训练项是否存在,于是用forEach循环调用上述findOne方法:

async create(createProgramDto: CreateProgramDto) {

  const { user, exercices } = createProgramDto;
  
  // some code

  exercices.forEach(async (element) => {
   await this.exerciceService.findOne(element.exercice)
  });

  const createProgram = new this.programModel(createProgramDto);
  return createProgram.save();
}

预期请求体中有训练项不存在时,服务会抛出NotFoundException,但实际出现未被正确捕获的错误:

/home/jeremy/src/apps/API/my-exercices/src/modules/exercice/exercice.service.ts:62
      throw new NotFoundException('exercice not found');
            ^
NotFoundException: exercice not found
    at ExerciceService.findOne (/home/jeremy/src/apps/API/my-exercices/src/modules/exercice/exercice.service.ts:62:13)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at /home/jeremy/src/apps/API/my-exercices/src/modules/program/program.service.ts:29:6

请求体示例:

{
    "title": "Test",
    "user": "634c1bd3c3d17e1b50c2b946",
    "exercices": [
        {
            "exercice": "637d116882ce1f7cc732d83c",
            "totalSet": "1",
            "rest": "1"
        },
        {
            "exercice": "637d116882ce1f7cc2d83c",
            "totalSet": "1",
            "rest": "1"
        }
    ]
}

单独调用findOne功能正常,但循环调用时无法按预期抛出异常,该怎么解决?

问题原因

forEach循环不支持异步函数的等待逻辑。在forEach里传入的异步箭头函数中,await只会阻塞函数自身,不会阻止外层的create函数继续执行。也就是说,create函数不等所有findOne调用完成,就直接执行创建并保存训练计划的代码了。而异步函数抛出的异常因为没有被正确捕获,就变成了未处理的Promise拒绝,导致报错。

解决方案

方式一:使用for...of循环(推荐)

for...of可以正确处理异步等待,确保所有训练项验证完成后才继续执行后续代码:

async create(createProgramDto: CreateProgramDto) {
  const { user, exercices } = createProgramDto;
  
  // some code

  // 替换forEach为for...of
  for (const element of exercices) {
    await this.exerciceService.findOne(element.exercice);
  }

  const createProgram = new this.programModel(createProgramDto);
  return createProgram.save();
}

方式二:使用Promise.all

如果希望并行验证所有训练项(效率更高,适合无依赖的异步操作),可以用Promise.all:

async create(createProgramDto: CreateProgramDto) {
  const { user, exercices } = createProgramDto;
  
  // some code

  // 并行执行所有findOne调用
  await Promise.all(
    exercices.map(element => this.exerciceService.findOne(element.exercice))
  );

  const createProgram = new this.programModel(createProgramDto);
  return createProgram.save();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:25:34