NestJS定时器之@Cron

NestJS定时器@Cron笔记

一、NestJS定时器概述

在NestJS中,任务调度可以在固定的日期/时间、重复的时间间隔之后,或者在指定的时间间隔之后执行任意代码(方法/函数)。Nest提供了@nestjs/schedule包,它集成了流行的Node.js cron包,方便开发者实现定时任务。

二、安装依赖

要开始使用任务调度功能,首先需要安装所需的依赖:

bash 复制代码
npm install --save @nestjs/schedule

三、启用任务调度

在NestJS的根模块(通常是AppModule)中导入ScheduleModule,并运行forRoot()静态方法来启用任务调度:

typescript 复制代码
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [ScheduleModule.forRoot()],
})
export class AppModule {}

.forRoot()调用会初始化调度器并注册应用程序中存在的任何声明式的cron jobs、timeouts和intervals。注册发生在onApplicationBootstrap生命周期钩子发生时,确保所有模块已加载并声明了任何计划的任务。

四、声明定时任务

1. 使用@Cron装饰器

使用@Cron()装饰器在包含要执行代码的方法定义之前声明一个cron任务。@Cron接受一个cron表达式和可选的配置对象。

typescript 复制代码
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('45 * * * * *')
  handleCron() {
    this.logger.debug('Called when the current second is 45');
  }
}

在这个例子中,每当当前秒数为45时,handleCron()方法都会被调用。换句话说,该方法将每分钟运行一次,在第45秒标记时运行。

2. Cron表达式

Cron表达式由6个字段组成(秒、分、小时、日期、月份、星期几),支持复杂的时间规则。

字段 范围 说明
0 - 59 每分钟的秒数
0 - 59 每小时的分钟数
小时 0 - 23 每天的小时数(24小时制)
日期 1 - 31 每月的日期
月份 1 - 12 或 JAN - DEC 月份
星期几 0 - 6 或 SUN - SAT 周几(0和7都表示周日)

特殊字符:

  • *:匹配任意值(如* * * * * *表示每秒执行)。
  • ,:指定多个值(如1,3,5表示秒为1、3、5时触发)。
  • -:范围(如10 - 15表示10到15秒)。
  • /:步长(如*/5表示每隔5秒)。

更多示例:

  • @Cron('0 0 12 * * *'):每天中午12:00执行
  • @Cron('0 0 9 - 17/2 * * *'):每天上午9点到下午5点,每2小时执行一次
  • @Cron('0 0 0 1 1 *'):每年1月1日00:00执行
  • @Cron('0 0 0 * * 0'):每周日00:00执行
3. 预定义的Cron模式

@nestjs/schedule包提供了一个方便的枚举,其中包含常用的cron模式。你可以按以下方式使用这个枚举:

typescript 复制代码
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron(CronExpression.EVERY_30_SECONDS)
  handleCron() {
    this.logger.debug('Called every 30 seconds');
  }
}

在这个例子中,handleCron()方法将每30秒调用一次。

4. 额外选项

可以将额外的选项作为第二个参数传递给@Cron()装饰器:

  • name:在声明后访问和控制cron任务非常有用。
  • timeZone:指定执行的时区。这将相对于你的时区修改实际时间。如果时区无效,会抛出错误。你可以在Moment时区网站上查看所有可用的时区。
  • utcOffset:这允许你指定你的时区偏移量,而不是使用timeZone参数。
  • disabled:这指示任务是否会被执行。
typescript 复制代码
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class NotificationService {
  @Cron('* * 0 * * *', { name: 'notifications', timeZone: 'Europe/Paris' })
  triggerNotifications() {}
}

五、声明间隔任务

要声明一个方法应该在(重复的)指定时间间隔内运行,将方法定义前缀为@Interval()装饰器。将时间间隔值作为毫秒数传递给装饰器:

typescript 复制代码
import { Injectable, Logger } from '@nestjs/common';
import { Interval } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Interval(10000)
  handleInterval() {
    this.logger.debug('Called every 10 seconds');
  }
}

本机制在底层使用JavaScript的setInterval()函数。

六、声明超时任务

要声明一个在指定时间后运行(一次)的方法,使用@Timeout()装饰器前缀。将从应用启动的相关时间偏移量(毫秒)传递给装饰器:

typescript 复制代码
import { Injectable, Logger } from '@nestjs/common';
import { Timeout } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Timeout(5000)
  handleTimeout() {
    this.logger.debug('Called once after 5 seconds');
  }
}

本机制在底层使用JavaScript的setTimeout()方法。

七、动态调度模块API

可以通过注入SchedulerRegistry来动态管理定时任务(如启动、停止、删除):

typescript 复制代码
import { Injectable } from '@nestjs/common';
import { SchedulerRegistry } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  constructor(private schedulerRegistry: SchedulerRegistry) {}

  stopTask(name: string) {
    const job = this.schedulerRegistry.getCronJob(name);
    job.stop();
  }

  deleteTask(name: string) {
    this.schedulerRegistry.deleteCronJob(name);
  }
}

八、与其他装饰器的对比

装饰器 用途 时间单位 示例
@Cron 固定时间执行任务 秒级 @Cron('0 0 12 * * *')
@Interval 间隔执行任务(毫秒级) 毫秒 @Interval(5000)
@Timeout 延迟执行一次任务(毫秒级) 毫秒 @Timeout(5000)

九、注意事项

  1. 任务并发:默认情况下,如果前一个任务尚未完成,下一个任务会立即开始。如果需要串行执行,需在任务逻辑中控制(如使用锁或队列)。
  2. 调试 :使用console.log或日志工具记录任务执行时间,确保cron表达式正确。
  3. 异常处理 :在任务方法中添加try - catch块,避免因异常导致任务崩溃。
  4. 测试 :可以使用setTimesetTimeout模拟时间变化,测试cron表达式是否符合预期。

十、完整示例

typescript 复制代码
import { Injectable, Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule';
import { Logger } from '@nestjs/common';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  constructor(private schedulerRegistry: SchedulerRegistry) {}

  @Cron(CronExpression.EVERY_5_SECONDS, { name: 'five - seconds - task', timeZone: 'Asia/Shanghai' })
  handleEveryFiveSeconds() {
    this.logger.debug('This runs every 5 seconds in Shanghai time');
  }

  @Cron('0 0 12 * * *', { name: 'daily - task' })
  handleDailyTask() {
    this.logger.debug('This runs daily at 12:00 PM UTC');
  }

  stopDailyTask() {
    const job = this.schedulerRegistry.getCronJob('daily - task');
    job.stop();
  }
}
相关推荐
None3214 天前
【NestJs】集成Prisma数据库配置
nestjs
濮水大叔9 天前
你认为Vonajs提供的这些特性会比Nestjs更好用吗?
nodejs·nestjs
前端卧龙人9 天前
你的nest项目不要再使用console.log
nestjs
plusone11 天前
【Nest指北系列-源码】(四)NestContainer
nestjs
zhuyasen13 天前
从Node.js到Go:如何从NestJS丝滑切换并拥抱Sponge框架
node.js·nestjs
前端杂货铺17 天前
NestJS——重构日志、数据库、配置
数据库·重构·nestjs
前端杂货铺25 天前
NestJS——日志、NestJS-logger、pino、winston、全局异常过滤器
nestjs·日志
林太白1 个月前
NestJS用户模块CRUD和分页实现
前端·javascript·nestjs
plusone1 个月前
【Nest指北系列-源码】(一)目录结构
nestjs