NestJS中Event Emitter重复触发问题求助:为何触发6次及解决方法?
NestJS Event Emitter 同一事件触发多次问题排查与解决
问题背景
调用一次事件触发代码:
this.eventEmitter.emit('company.created', companyCreatedHistory);
但@OnEvent('company.created')装饰的监听方法被触发了6次,监听代码如下:
@Injectable() export class EntityCreatedListener { constructor(private readonly historyService: HistoriesService) {} @OnEvent('company.created') handleCompanyCreatedEvent(eventObject: typeof eventEmitterObject) { console.log('Hi') this.createAuditLog(eventObject, 'company.created'); } }
常见原因
- 监听器被多次实例化:若
EntityCreatedListener被多个模块重复添加到providers,或在模块中多次声明,Nest会创建多个监听器实例,每个实例都会响应事件。 - 事件发射器非单例:手动创建多个
EventEmitter实例,而非使用Nest提供的全局单例,会导致一次emit触发所有实例的监听逻辑。 - 热重载重复注册:开发环境热重载(HMR)时,模块重新加载会导致监听器被重复注册到事件发射器。
- 全局中间件/拦截器重复触发:全局中间件或拦截器中存在逻辑漏洞,导致事件被多次触发或请求被重复处理。
解决方法
确保监听器单例存在
检查模块配置,EntityCreatedListener仅在一个模块的providers中声明,其他模块通过imports导入该模块,禁止重复添加。示例:@Module({ imports: [EventEmitterModule.forRoot()], providers: [EntityCreatedListener, CompanyService], exports: [EntityCreatedListener] // 供其他模块导入使用 }) export class AuditModule {}其他模块只需导入
AuditModule即可,无需再次将EntityCreatedListener加入providers。使用Nest官方单例EventEmitter
确保通过依赖注入获取EventEmitter实例,而非手动创建:import { EventEmitter2 } from '@nestjs/event-emitter'; @Injectable() export class CompanyService { constructor(private readonly eventEmitter: EventEmitter2) {} createCompany() { // ...业务逻辑 this.eventEmitter.emit('company.created', companyCreatedHistory); } }修复热重载问题
- 开发环境测试时可临时禁用热重载,确认是否是热重载导致的重复注册。
- 在监听器中实现
OnModuleDestroy钩子,手动移除事件监听:import { Injectable, OnModuleDestroy } from '@nestjs/common'; import { OnEvent, EventEmitter2 } from '@nestjs/event-emitter'; @Injectable() export class EntityCreatedListener implements OnModuleDestroy { constructor( private readonly historyService: HistoriesService, private readonly eventEmitter: EventEmitter2 ) {} @OnEvent('company.created') handleCompanyCreatedEvent(eventObject: typeof eventEmitterObject) { console.log('Hi') this.createAuditLog(eventObject, 'company.created'); } onModuleDestroy() { this.eventEmitter.off('company.created', this.handleCompanyCreatedEvent.bind(this)); } }
排查全局中间件/拦截器
检查全局中间件、拦截器或守卫的逻辑,确保没有重复触发事件或导致请求被多次执行的代码。
内容的提问来源于stack exchange,提问作者Shehan Silva
相关产品推荐
相关产品推荐

