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();
  }
}
相关推荐
Eric_见嘉3 天前
NestJS 🧑‍🍳 厨子必修课(九):API 文档 Swagger
前端·后端·nestjs
XiaoYu200212 天前
第3章 Nest.js拦截器
前端·ai编程·nestjs
XiaoYu200213 天前
第2章 Nest.js入门
前端·ai编程·nestjs
实习生小黄14 天前
NestJS 调试方案
后端·nestjs
当时只道寻常17 天前
NestJS 如何配置环境变量
nestjs
濮水大叔1 个月前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
ovensi1 个月前
告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南
nestjs
ovensi1 个月前
Docker+NestJS+ELK:从零搭建全链路日志监控系统
后端·nestjs
Gogo8161 个月前
nestjs 的项目启动
nestjs