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
相关产品推荐
相关产品推荐

