任务调度允许您安排任意代码(方法/函数)在固定日期/时间、按重复间隔或在指定间隔后执行一次。在 Linux 领域,这通常由操作系统层面的 cron 等包处理。对于 Node.js 应用,有多个包可模拟类似 cron 的功能。Nest 提供了 @nestjs/schedule
包,它与流行的 Node.jscron 包集成。我们将在本章介绍这个包。
要开始使用它,我们首先需要安装所需的依赖项。
要激活任务调度功能,请将 ScheduleModule
导入根模块 AppModule
,并运行如下所示的 forRoot()
静态方法:
.forRoot()
调用会初始化调度器并注册应用中所有声明式的 cron 任务 、 超时任务 和 间隔任务 。注册过程发生在 onApplicationBootstrap
生命周期钩子触发时,确保所有模块都已加载并声明了计划任务。
cron 任务会调度任意函数(方法调用)自动执行。cron 任务可以:
使用 @Cron()
装饰器在包含待执行代码的方法定义前声明定时任务,如下所示:
在此示例中,每当当前秒数为 45
时,handleCron()
方法就会被调用。换句话说,该方法将在每分钟的第 45 秒运行一次。
@Cron()
装饰器支持以下标准 cron 表达式 :
*
)1-3,5
)*/2
)在上面的示例中,我们向装饰器传递了 45 * * * * *
。下表展示了 cron 模式字符串中每个位置的解析方式:
一些示例的 cron 模式包括:
@nestjs/schedule
包提供了一个包含常用 cron 模式的便捷枚举。您可以按如下方式使用此枚举:
在此示例中,handleCron()
方法将每 30
秒调用一次。如果发生异常,它将被记录到控制台,因为每个用 @Cron()
注解的方法都会自动包装在 try-catch
代码块中。
或者,你也可以向 @Cron()
装饰器传入一个 JavaScript 的 Date
对象。这样做会使任务在指定日期精确执行一次。
提示 使用 JavaScript 日期运算来安排相对于当前日期的任务。例如,
@Cron(new Date(Date.now() + 10 * 1000))
可以让任务在应用启动 10 秒后运行。
此外,你还可以将额外选项作为第二个参数传给 @Cron()
装饰器。
选项 | 描述 |
---|---|
name |
这在声明后访问和控制 cron 任务时非常有用。 |
timeZone |
指定执行时区。这将根据您的时区调整实际时间。若时区无效,将抛出错误。您可在 Moment Timezone 网站查看所有可用时区。 |
utcOffset |
此选项允许您直接指定时区偏移量,而无需使用 timeZone 参数。 |
waitForCompletion |
若设为 true,当前 onTick 回调完成前将不会运行该定时任务的其他实例。当前任务执行期间所有新触发的计划执行都将被跳过。 |
disabled |
此参数表示该任务是否会被执行。 |
您可以在声明后访问和控制 cron 任务,或者通过动态 API 动态创建 cron 任务(其 cron 模式在运行时定义)。要通过 API 访问声明式 cron 任务,您必须通过装饰器的第二个可选参数对象中的 name
属性为任务关联名称。
要声明某个方法应以(重复的)指定间隔运行,请在方法定义前添加 @Interval()
装饰器。如下所示,将间隔值(以毫秒为单位的数字)传递给装饰器:
提示 此机制底层使用 JavaScript 的
setInterval()
函数。您也可以使用 cron 任务来安排重复作业。
若要通过动态 API 在声明类外部控制声明式间隔,请使用以下构造将间隔与名称关联:
如果发生异常,它将被记录到控制台,因为每个用 @Interval()
注解的方法都会自动包裹在 try-catch
代码块中。
动态 API 还支持创建动态间隔(其属性在运行时定义),以及列出和删除这些间隔。
要在指定超时时间运行(一次)方法,请在方法定义前添加 @Timeout()
装饰器。如下所示,将相对于应用启动的时间偏移量(以毫秒为单位)传递给装饰器:
提示 该机制底层使用了 JavaScript 的
setTimeout()
函数。
如果发生异常,它将被记录到控制台,因为每个用 @Timeout()
注解的方法都会自动被包裹在 try-catch
代码块中。
若要通过动态 API 在声明类外部控制声明式超时,请使用以下构造将超时与名称关联:
动态 API 还支持创建动态超时,其属性在运行时定义,并能列出和删除这些超时。
@nestjs/schedule
模块提供的动态 API 可管理声明式定时任务 、 超时和间隔 。该 API 还支持创建和管理动态定时任务、超时及间隔,这些元素的属性均在运行时定义。
通过 SchedulerRegistry
API,你可以在代码的任何位置根据名称获取 CronJob
实例的引用。首先,使用标准的构造函数注入方式注入 SchedulerRegistry
:
提示 从
@nestjs/schedule
包中导入SchedulerRegistry
。
然后在类中按如下方式使用。假设已通过以下声明创建了一个 cron job:
通过以下方式访问该 job:
getCronJob()
方法返回指定名称的定时任务。返回的 CronJob
对象包含以下方法:
stop()
- 停止已计划运行的任务。start()
- 重新启动已停止的任务。setTime(time: CronTime)
- 停止任务,为其设置新的时间,然后重新启动lastDate()
- 返回作业最后一次执行日期的 DateTime
表示形式。nextDate()
- 返回作业下一次计划执行日期的 DateTime
表示形式。nextDates(count: number)
- 提供一组(大小为 count
)DateTime
表示形式,包含接下来会触发作业执行的日期集合。count
默认为 0,此时返回空数组。info 提示 在
DateTime
对象上使用toJSDate()
可将其渲染为与此 DateTime 等效的 JavaScript Date 对象。
创建一个新的定时任务,可通过动态调用 SchedulerRegistry#addCronJob
方法实现,如下所示:
在这段代码中,我们使用 CronJob
对象(来自 cron
包)来创建定时任务。CronJob
构造函数接收两个参数:第一个是 cron 表达式(与 @Cron()
装饰器的格式相同),第二个是定时触发器触发时执行的回调函数。SchedulerRegistry#addCronJob
方法同样接收两个参数:定时任务的名称和 CronJob
对象本身。
警告 请记得在使用前先注入
SchedulerRegistry
。同时需要从cron
包中导入CronJob
。
删除指定名称的定时任务可通过 SchedulerRegistry#deleteCronJob
方法实现,如下所示:
列出所有使用 SchedulerRegistry#getCronJobs
方法的定时任务:
getCronJobs()
方法返回一个 map
。在这段代码中,我们遍历该映射并尝试访问每个 CronJob
的 nextDate()
方法。在 CronJob
API 中,如果任务已触发且没有未来的触发日期,则会抛出异常。
通过 SchedulerRegistry#getInterval
方法获取间隔引用。如上所述,使用标准构造函数注入 SchedulerRegistry
:
使用方法如下:
使用 SchedulerRegistry#addInterval
方法动态创建新定时器,如下所示:
在这段代码中,我们创建了一个标准的 JavaScript 定时器,然后将其传递给 SchedulerRegistry#addInterval
方法。该方法接收两个参数:定时器名称和定时器实例本身。
使用 SchedulerRegistry#deleteInterval
方法删除已命名的定时器,如下所示:
列出所有使用 SchedulerRegistry#getIntervals
方法的间隔如下:
通过 SchedulerRegistry#getTimeout
方法获取超时引用。如上所述,使用标准构造函数注入 SchedulerRegistry
:
并按如下方式使用:
创建一个新的动态超时,使用 SchedulerRegistry#addTimeout
方法,如下所示:
在这段代码中,我们创建了一个标准的 JavaScript 超时,然后将其传递给 SchedulerRegistry#addTimeout
方法。该方法接收两个参数:超时名称和超时实例本身。
删除一个命名超时,使用 SchedulerRegistry#deleteTimeout
方法,如下所示:
列出所有超时,使用 SchedulerRegistry#getTimeouts
方法,如下所示:
一个可用的示例在此处查看。