NestJS小技巧14-发送和订阅事件(Event Emitter)

1,465 阅读5分钟
by 雪隐 from https://juejin.cn/user/1433418895994094
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权

原文链接

在这篇教程文章中,我将演示如何使用事件发射器来处理和实现NestJS框架的一些任务。这是一个初学者友好的教程,您不需要掌握任何框架的高级知识。

在构建应用程序时,我们有时要求在发生某种“其他操作”之后执行某些操作。例如,在电子商务应用程序中,我们可能希望在付款后处理订单的交付(将订单转发并分配给正确的物流团队)。您真的不希望用户为交付处理而烦恼,您希望这是异步完成的。

NestJs内置了对这类操作的支持,我将在本文中向您展示如何做到这一点。我们首先使用NestJS cli创建一个新的应用程序

nest new nestjs-event-tutorial  
  
? Which package manager would you ❤️ to use? (Use arrow keys)  
❯ npm  
  yarn  
  pnpm

选择所需的程序包管理器,然后等待新应用程序启动。项目设置完成后,将cd放入新的项目目录cd nestjs event tutorialand启动应用程序:npm run start:dev

[19:20:09] Starting compilation in watch mode...

[19:20:11] Found 0 errors. Watching for file changes.

[Nest] 1401  - 2023/05/05 19:20:12     LOG [NestFactory] Starting Nest application...
[Nest] 1401  - 2023/05/05 19:20:12     LOG [InstanceLoader] DiscoveryModule dependencies initialized +32ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [InstanceLoader] EventEmitterModule dependencies initialized +0ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [RoutesResolver] AppController {/}: +6ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [RouterExplorer] Mapped {/register, POST} route +1ms
[Nest] 1401  - 2023/05/05 19:20:12     LOG [NestApplication] Nest application successfully started +3ms

我们将要处理的场景是,我们希望在完成注册后向我们系统的任何新用户发送一封欢迎电子邮件。我们将使用事件发射器的概念,这使我们能够解耦应用程序的各个方面。一个事件可以有几个相互独立的侦听器,即每个侦听器可以以不同的方式处理该事件。NestJS有一个事件发射器包,它允许我们在应用程序中发送和订阅事件

import { Module } from '@nestjs/common';  
import { EventEmitterModule } from '@nestjs/event-emitter';  
import { AppController } from './app.controller';  
import { AppService } from './app.service';  
  
@Module({  
imports: [EventEmitterModule.forRoot()],  
controllers: [AppController],  
providers: [AppService],  
})  
export class AppModule {}

从上面开始,我们将EventEmitterModule从NestJS事件发射器包导入到应用程序模块中,并调用静态forRoot方法,该方法初始化事件发射器并注册应用程序中存在的任何声明性事件侦听器。

接下来,我们需要安装nestjs事件发射器依赖项:@nestjs/event-emitter

pnpm i --save @nestjs/event-emitter

现在,让我们在app.controller.ts中创建一个新的POST端点/register,用于注册新用户。端点应将用户电子邮件和密码作为有效负载。

import { Body, Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';

class RegisterDto {
  email: string;
  password: string;
}

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Post('/register')
  async register(@Body() registerBody: RegisterDto): Promise<void> {
    return this.appService.register(registerBody);
  }
}

然后在app.service.ts中,创建register方法。我们还通过类构造函数注入EventEmitter2,这是我们用来触发事件的。

import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';

class RegisterDto {
  email: string;
  password: string;
}

class UserRegisteredEvent {
  constructor(public readonly userId: string, public readonly email: string) {}
}

@Injectable()
export class AppService {
  constructor(private readonly eventEmitter: EventEmitter2) {}
  private readonly logger = new Logger(AppService.name);
  getHello(): string {
    return 'Hello World!';
  }

  async register(payload: RegisterDto): Promise<void> {
    this.logger.log('Registering user...', payload);
    await new Promise<void>((resolve) => setTimeout(() => resolve(), 5000));
    this.logger.log('New user registered...', payload.email);
    this.eventEmitter.emit(
      'new_registered_user',
      new UserRegisteredEvent('12345', payload.email),
    );
  }
}

从上面的片段来看:

  1. 我们创建了一个RegisterDto类,该类将emailpassword定义为字符串。

  2. 我们创建另一个类:UserRegisteredEvent

  3. 我们创建了一个register方法,模拟一个需要5秒才能完成的过程,就像用户在服务器上完成注册所需的时间一样。然后,我们使用eventEmitter进行发送,并调用名为“new_registered_user”的方法,其有效载荷payloadUserRegisteredEvent类中定义

现在,让我们用npm run start:dev在3000端口启动我们的应用程序,并用postman调用/register端点

image.png

image.png 请注意终端中的日志。我们在5秒后注册了一个新用户,新用户的电子邮件也被记录了下来。

现在,我们已经能够在成功注册新用户时触发事件,下一步将是设置侦听器。要做到这一点,我们需要使用nestjs/event-emmitter包中的OnEvent装饰器。

import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';

class UserRegisteredEvent {
  constructor(public readonly userId: string, public readonly email: string) {}
}

@Injectable()
export class TestService {
  constructor(private readonly eventEmitter: EventEmitter2) {}
  private readonly logger = new Logger(TestService.name);

  @OnEvent('new_registered_user', { async: true })
  async sendWelcomeEmal(payload: UserRegisteredEvent) {
    this.logger.log('Sending mail to welcome new user', payload.email);
    await new Promise<void>((resolve) => setTimeout(() => resolve(), 3000));
    this.logger.log('Mail sent to new user', payload.email);
  }
}

从上面的片段来看:

  1. 我们将OnEvent添加到依赖项导入中。

  2. 使用OnEvent装饰器创建一个新的sendWelcomeEmail方法,并将事件名称指定为new_registered_user,以表明此方法是一个事件侦听器,并且只有在应用程序中的任何位置触发/发出特定事件时才会调用。

  3. 我们需要指示监听器是否是装饰器上的异步函数。在这种情况下,我们将其作为一个异步函数,在3s后解析promise

现在,让我们在postman上再次提出请求,并注意终端中的更改

image.png

在这里,我们可以看到我们的事件被触发了,监听器也监听了该事件,并且邮件被发送给了我们的新用户。

就是这样,这就是如何用NestJs触发事件,并创建侦听器来监听事件。您可以从官方文档中了解更多信息。请随时发表您对这篇文章的评论和意见。如果觉得这篇对您有帮助,请记得点赞/收藏!

感谢阅读。

本章代码

代码