Planification des tâches
La planification des tâches vous permet de programmer du code arbitraire (méthodes/fonctions) pour s’exécuter à une date/heure fixe, à intervalles réguliers, ou une fois après un intervalle spécifié. Dans le monde Linux, cela est souvent géré par des paquets comme cron au niveau du système d’exploitation. Pour les applications Node.js, plusieurs paquets imitent des fonctionnalités similaires à cron. Nest fournit le paquet @nestjs/schedule
, qui s’intègre au célèbre paquet Node.js cron. Nous couvrirons ce paquet dans le chapitre actuel.
Installation
Pour commencer à l’utiliser, nous installons d’abord les dépendances requises.
$ npm install --save @nestjs/schedule
Pour activer la planification des tâches, importez le ScheduleModule
dans le module racine AppModule
et exécutez la méthode statique forRoot()
comme indiqué ci-dessous :
import { Module } from '@nestjs/common';import { ScheduleModule } from '@nestjs/schedule';
@Module({ imports: [ ScheduleModule.forRoot() ],})export class AppModule {}
L’appel forRoot()
initialise le planificateur et enregistre toute déclaration de cron jobs, timeouts et intervals qui existent dans votre application. L’enregistrement se produit lors de l’appel du hook de cycle de vie onApplicationBootstrap
, assurant que tous les modules ont été chargés et ont déclaré les travaux planifiés.
Cron jobs déclaratifs
Un cron job programme une fonction arbitraire (appel de méthode) pour s’exécuter automatiquement. Les cron jobs peuvent s’exécuter :
- Une fois, à une date/heure spécifiée.
- De manière récurrente ; les travaux récurrents peuvent s’exécuter à un instant spécifié dans un intervalle spécifié (par exemple, une fois par heure, une fois par semaine, une fois toutes les 5 minutes).
Déclarez un cron job avec le décorateur @Cron()
précédant la définition de méthode contenant le code à exécuter, comme suit :
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('Appelé lorsque la seconde actuelle est 45'); }}
Dans cet exemple, la méthode handleCron()
sera appelée chaque fois que la seconde actuelle est 45
. En d’autres termes, la méthode sera exécutée une fois par minute, à la marque de 45 secondes.
Le décorateur @Cron()
prend en charge les modèles cron standards suivants :
- Asterisk (par exemple,
*
) - Ranges (par exemple,
1-3,5
) - Steps (par exemple,
*/2
)
Dans l’exemple ci-dessus, nous avons passé 45 * * * * *
au décorateur. La clé suivante montre comment chaque position dans la chaîne de modèle cron est interprétée :
* * * * * *| | | | | || | | | | | day of week| | | | months| | | day of month| | hours| minutes| seconds (facultatif)
Voici quelques exemples de modèles cron :
Modèle | Description |
---|---|
* * * * * * | chaque seconde |
45 * * * * * | chaque minute, à la 45ème seconde |
0 10 * * * * | chaque heure, au début de la 10ème minute |
0 */30 9-17 * * * | toutes les 30 minutes entre 9h et 17h |
0 30 11 * * 1-5 | Du lundi au vendredi à 11h30 |
Le paquet @nestjs/schedule
fournit une énumération pratique avec des modèles cron couramment utilisés. Vous pouvez utiliser cette énumération comme suit :
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('Appelé toutes les 30 secondes'); }}
Dans cet exemple, la méthode handleCron()
sera appelée toutes les 30
secondes.
Alternativement, vous pouvez fournir un objet JavaScript Date
au décorateur @Cron()
. Cela provoque l’exécution du travail exactement une fois, à la date spécifiée.
Vous pouvez également fournir des options supplémentaires en tant que deuxième paramètre au décorateur @Cron()
.
Option | Description |
---|---|
name | Utile pour accéder et contrôler un cron job après qu’il a été déclaré. |
timeZone | Spécifiez le fuseau horaire pour l’exécution. Cela modifiera l’heure réelle par rapport à votre fuseau horaire. Si le fuseau horaire est invalide, une erreur est levée. Vous pouvez vérifier tous les fuseaux horaires disponibles sur le site Moment Timezone. |
utcOffset | Cela vous permet de spécifier le décalage de votre fuseau horaire plutôt que d’utiliser le paramètre timeZone . |
disabled | Indique si le travail sera exécuté ou non. |
import { Injectable } from '@nestjs/common';import { Cron, CronExpression } from '@nestjs/schedule';
@Injectable()export class NotificationService { @Cron('* * 0 * * *', { name: 'notifications', timeZone: 'Europe/Paris', }) triggerNotifications() { // Logic to trigger notifications }}
Vous pouvez accéder et contrôler un cron job après qu’il a été déclaré, ou créer dynamiquement un cron job (où son modèle cron est défini à l’exécution) avec l’API dynamique. Pour accéder à un cron job déclaratif via l’API, vous devez associer le travail avec un nom en passant la propriété name
dans un objet d’options facultatif en tant que deuxième argument du décorateur.
Intervalles déclaratifs
Pour déclarer qu’une méthode doit s’exécuter à un intervalle (récurrent) spécifié, préfixez la définition de la méthode avec le décorateur @Interval()
. Passez la valeur de l’intervalle, en tant que nombre en millisecondes, au décorateur comme indiqué ci-dessous :
import { Injectable } from '@nestjs/common';import { Interval } from '@nestjs/schedule';
@Injectable()export class TasksService { @Interval(10000) handleInterval() { // Logic to execute every 10 seconds }}
Si vous souhaitez contrôler votre intervalle déclaratif depuis l’extérieur de la classe déclarative via l’API dynamique, associez l’intervalle avec un nom en utilisant la construction suivante :
import { Injectable } from '@nestjs/common';import { Interval } from '@nestjs/schedule';
@Injectable()export class TasksService { @Interval('notifications', 2500) handleInterval() { // Logic to execute }}
L’API dynamique permet également de créer des intervalles dynamiques, où les propriétés de l’intervalle sont définies à l’exécution, et de lister et supprimer ces intervalles.
API dynamique du module de planification
Le module @nestjs/schedule
fournit une API dynamique qui permet de gérer les cron jobs déclaratifs, les timeouts déclaratifs et les intervalles déclaratifs. L’API permet également de créer et gérer des cron jobs, timeouts et intervalles dynamiques, où les propriétés sont définies à l’exécution.
Cron jobs dynamiques
Obtenez une référence à une instance CronJob
par nom depuis n’importe où dans votre code en utilisant l’API SchedulerRegistry
. Tout d’abord, injectez SchedulerRegistry
à l’aide de l’injection de constructeur standard :
import { SchedulerRegistry } from '@nestjs/schedule';
constructor(private schedulerRegistry: SchedulerRegistry) {}
Ensuite, utilisez-le dans une classe comme suit. Supposons qu’un cron job ait été créé avec la déclaration suivante :
@Cron('* * 8 * * *', { name: 'notifications',})triggerNotifications() { // Logic to trigger notifications}
Accédez à ce job en utilisant :
const job = this.schedulerRegistry.getCronJob('notifications');job.stop();console.log(job.lastDate());
La méthode getCronJob()
retourne le cron job nommé. L’objet CronJob
retourné possède les méthodes suivantes :
stop()
- arrête un travail prévu pour s’exécuter.start()
- redémarre un travail qui a été arrêté.setTime(time: CronTime)
- arrête un travail, fixe un nouveau temps pour celui-ci, puis le redémarre.lastDate()
- retourne une représentationDateTime
de la date à laquelle la dernière exécution d’un travail a eu lieu.nextDate()
- retourne une représentationDateTime
de la date à laquelle la prochaine exécution d’un travail est prévue.nextDates(count: number)
- fournit un tableau (taillecount
) de représentationsDateTime
pour le prochain ensemble de dates qui déclencheront l’exécution du travail.count
par défaut est 0, renvoyant un tableau vide.
Créer un nouveau cron job de manière dynamique en utilisant la méthode SchedulerRegistry#addCronJob
, comme suit :
addCronJob(name: string, seconds: string) { const job = new CronJob(`\${seconds} * * * * *`, () => { this.logger.warn(`time (\${seconds}) for job \${name} to run!`); });
this.schedulerRegistry.addCronJob(name, job); job.start();
this.logger.warn(`job \${name} added for each minute at \${seconds} seconds!`);}
Dans ce code, nous utilisons l’objet CronJob
du paquet cron
pour créer le cron job. Le constructeur CronJob
prend un modèle cron (tout comme le décorateur @Cron()
) comme premier argument, et un rappel à exécuter lorsque le timer cron se déclenche comme second argument. La méthode SchedulerRegistry#addCronJob
prend deux arguments : un nom pour le CronJob
, et l’objet CronJob
lui-même.
Supprimez un cron job nommé en utilisant la méthode SchedulerRegistry#deleteCronJob
, comme suit :
deleteCron(name: string) { this.schedulerRegistry.deleteCronJob(name); this.logger.warn(`job \${name} deleted!`);}
Listez tous les cron jobs en utilisant la méthode SchedulerRegistry#getCronJobs
comme suit :
getCrons() { const jobs = this.schedulerRegistry.getCronJobs(); jobs.forEach((value, key, map) => { let next; try { next = value.nextDate().toJSDate(); } catch (e) { next = 'error: next fire date is in the past!'; } this.logger.log(`job: \${key} -> next: \${next}`); });}
La méthode getCronJobs()
retourne une map
. Dans ce code, nous parcourons la carte et tentons d’accéder à la méthode nextDate()
de chaque CronJob
. Dans l’API CronJob
, si un travail a déjà été exécuté et n’a aucune date d’exécution future, cela lève une exception.
Intervalles dynamiques
Obtenez une référence à un intervalle avec la méthode SchedulerRegistry#getInterval
. Comme précédemment, injectez SchedulerRegistry
en utilisant l’injection de constructeur standard :
constructor(private schedulerRegistry: SchedulerRegistry) {}
Et utilisez-le comme suit :
const interval = this.schedulerRegistry.getInterval('notifications');clearInterval(interval);
Créer un nouvel intervalle de manière dynamique en utilisant la méthode SchedulerRegistry#addInterval
, comme suit :
addInterval(name: string, milliseconds: number) { const callback = () => { this.logger.warn(`Interval \${name} executing at time (\${milliseconds})!`); };
const interval = setInterval(callback, milliseconds); this.schedulerRegistry.addInterval(name, interval);}
Dans ce code, nous créons un intervalle JavaScript standard, puis le passons à la méthode SchedulerRegistry#addInterval
. Cette méthode prend deux arguments : un nom pour l’intervalle et l’intervalle lui-même.
Supprimez un intervalle nommé en utilisant la méthode SchedulerRegistry#deleteInterval
, comme suit :
deleteInterval(name: string) { this.schedulerRegistry.deleteInterval(name); this.logger.warn(`Interval \${name} deleted!`);}
Listez tous les intervalles en utilisant la méthode SchedulerRegistry#getIntervals
comme suit :
getIntervals() { const intervals = this.schedulerRegistry.getIntervals(); intervals.forEach(key => { this.logger.log(`Interval: \${key}`); });}
Timeouts dynamiques
Obtenez une référence à un timeout avec la méthode SchedulerRegistry#getTimeout
. Comme précédemment, injectez SchedulerRegistry
en utilisant l’injection de constructeur standard :
constructor(private schedulerRegistry: SchedulerRegistry) {}
Et utilisez-le comme suit :
const timeout = this.schedulerRegistry.getTimeout('notifications');clearTimeout(timeout);
Créer un nouveau timeout de manière dynamique en utilisant la méthode SchedulerRegistry#addTimeout
, comme suit :
addTimeout(name: string, milliseconds: number) { const callback = () => { this.logger.warn(`Timeout \${name} executing after (\${milliseconds})!`); };
const timeout = setTimeout(callback, milliseconds); this.schedulerRegistry.addTimeout(name, timeout);}
Dans ce code, nous créons un timeout JavaScript standard, puis le passons à la méthode SchedulerRegistry#addTimeout
. Cette méthode prend deux arguments : un nom pour le timeout et le timeout lui-même.
Supprimez un timeout nommé en utilisant la méthode SchedulerRegistry#deleteTimeout
, comme suit :
deleteTimeout(name: string) { this.schedulerRegistry.deleteTimeout(name); this.logger.warn(`Timeout \${name} deleted!`);}
Listez tous les timeouts en utilisant la méthode SchedulerRegistry#getTimeouts
comme suit :
getTimeouts() { const timeouts = this.schedulerRegistry.getTimeouts(); timeouts.forEach(key => { this.logger.log(`Timeout: \${key}`); });}
Exemple
Un exemple fonctionnel est disponible ici.