NestJS Logo

Planification des tâches

La planification des tâches vous permet de programmer l'exécution d'un code arbitraire (méthodes/fonctions) à une date/heure fixe, à des intervalles récurrents ou une fois après un intervalle spécifié. Dans le monde Linux, cela est souvent géré par des packages comme cron au niveau du système d'exploitation. Pour les applications Node.js, il existe plusieurs packages qui émulent les fonctionnalités de type cron. Nest fournit le package @nestjs/schedule, qui s'intègre au populaire package Node.js cron. Nous aborderons ce package dans le présent chapitre.

Installation#

Pour commencer à l'utiliser, nous devons d'abord installer les dépendances nécessaires.


$ npm install --save @nestjs/schedule

Pour activer la planification des tâches, importez le module ScheduleModule dans le module racine AppModule et exécutez la méthode statique forRoot() comme indiqué ci-dessous :

app.module.ts
JS TS

import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

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

L'appel à .forRoot() initialise l'ordonnanceur et enregistre tous les tâches cron, délais et intervalles déclaratifs qui existent dans votre application. L'enregistrement a lieu lorsque le hook du cycle de vie onApplicationBootstrap se produit, ce qui permet de s'assurer que tous les modules ont été chargés et qu'ils ont déclaré tous les travaux programmés.

Tâches cron déclaratives#

Un cron job planifie l'exécution automatique d'une fonction arbitraire (appel de méthode). Les tâches cron peuvent s'exécuter :

  • Une fois, à une date/heure donnée.
  • Sur une base récurrente ; les tâches récurrentes peuvent être exécutées à un moment précis dans un intervalle donné (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 la 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 courante est à 45');
  }
}

Dans cet exemple, la méthode handleCron() sera appelée chaque fois que la seconde courante est 45. En d'autres termes, la méthode sera exécutée une fois par minute, au bout de 45 secondes.

Le décorateur @Cron() supporte les motifs cron standards suivants :

  • Astérisque (par exemple *)
  • Plages (par exemple 1-3,5)
  • Pas (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 caractères du motif cron est interprétée :


* * * * * *
| | | | | |
| | | | | jour de la semaine
| | | | mois
| | jour du mois
| heures
| minutes
secondes (facultatif)

Voici quelques exemples de modèles de cron :

* * * * * *chaque seconde
45 * * * * *toutes les minutes, à la 45e seconde
0 10 * * * *toutes les heures, au début de la 10e minute
0 */30 9-17 * * *toutes les 30 minutes entre 9h et 17h
0 30 11 * * 1-5Du lundi au vendredi à 11h30

Le package @nestjs/schedule fournit un enum pratique avec des modèles de cron couramment utilisés. Vous pouvez utiliser cette liste 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. Si une exception se produit, elle sera enregistrée dans la console, car chaque méthode annotée avec @Cron() est automatiquement enveloppée dans un bloc try-catch.

Vous pouvez également fournir un objet JavaScript Date au décorateur @Cron(). Dans ce cas, la tâche sera exécutée une seule fois, à la date spécifiée.

Astuce Utilisez l'arithmétique de date JavaScript pour planifier des tâches par rapport à la date actuelle. Par exemple, @Cron(new Date(Date.now() + 10 * 1000)) pour programmer une tâche à exécuter 10 secondes après le démarrage de l'application.

Vous pouvez également fournir des options supplémentaires en tant que second paramètre du décorateur @Cron().

name Utile pour accéder à une tâche cron et la contrôler après qu'elle a été déclarée.
timeZone Spécifiez le fuseau horaire pour l'exécution. Ceci modifiera l'heure actuelle par rapport à votre fuseau horaire. Si la zone horaire n'est pas valide, une erreur est générée. Vous pouvez consulter 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.
waitForCompletion Si true, aucune instance supplémentaire de la tâche cron ne sera exécutée tant que le callback onTick actuel n'aura pas été exécuté. Toutes les nouvelles exécutions programmées qui se produisent pendant que la tâche cron actuelle est en cours d'exécution seront entièrement ignorées.
disabled Ceci indique si la tâche sera exécutée 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() {}
}

Vous pouvez accéder et contrôler une tâche cron après qu'elle ait été déclarée, ou créer dynamiquement une tâche cron (où son modèle cron est défini à l'exécution) avec l'Dynamic API. Pour accéder à une tâche cron déclarative via l'API, vous devez associer la tâche à un nom en passant la propriété name dans un objet optionnel d'options 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 spécifié (récurrent), préfixez la définition de la méthode avec le décorateur @Interval(). Passez la valeur de l'intervalle, sous la forme d'un nombre en millisecondes, au décorateur comme indiqué ci-dessous :


@Interval(10000)
handleInterval() {
  this.logger.debug('Appelé toutes les 10 secondes');
}
Astuce Ce mécanisme utilise la fonction JavaScript setInterval() sous le capot. Vous pouvez également utiliser une tâche cron pour programmer des tâches récurrentes.

Si vous souhaitez contrôler votre intervalle déclaratif depuis l'extérieur de la classe déclarante via l'API dynamique, associez un nom à l'intervalle à l'aide de la construction suivante :


@Interval('notifications', 2500)
handleInterval() {}

Si une exception se produit, elle sera enregistrée dans la console, car chaque méthode annotée avec @Interval() est automatiquement enveloppée dans un bloc try-catch.

L'API dynamique permet également de créer des intervalles dynamiques, dont les propriétés sont définies au moment de l'exécution, et de lister et supprimer ces intervalles.

Délais déclaratifs#

Pour déclarer qu'une méthode doit s'exécuter (une fois) à un délai spécifié, préfixez la définition de la méthode avec le décorateur @Timeout(). Passez le décalage temporel relatif (en millisecondes), à partir du démarrage de l'application, au décorateur comme indiqué ci-dessous :


@Timeout(5000)
handleTimeout() {
  this.logger.debug('Appelé une fois après 5 secondes');
}
Astuce Ce mécanisme utilise la fonction JavaScript setTimeout() sous le capot.

Si une exception se produit, elle sera enregistrée dans la console, car chaque méthode annotée avec @Timeout() est automatiquement enveloppée dans un bloc try-catch.

Si vous souhaitez contrôler votre délai déclaratif depuis l'extérieur de la classe déclarante via l'API dynamique, associez le délai à un nom à l'aide de la construction suivante :


@Timeout('notifications', 2500)
handleTimeout() {}

L'API dynamique permet également de créer des délais dynamiques, dont les propriétés sont définies au moment de l'exécution, et de lister et supprimer ces délais.

API du module de programmation dynamique#

Le module @nestjs/schedule fournit une API dynamique qui permet d'administrer les tâches cron, délais et intervalles déclaratifs. L'API permet également de créer et de gérer des tâches cron dynamiques, des délais et des intervalles, dont les propriétés sont définies au moment de l'exécution.

Tâches cron dynamiques#

Obtenir une référence à une instance de CronJob par son nom depuis n'importe quel endroit de votre code en utilisant l'API SchedulerRegistry. Tout d'abord, injectez SchedulerRegistry en utilisant l'injection de constructeur standard :


constructor(private schedulerRegistry: SchedulerRegistry) {}
Astuce Importez le SchedulerRegistry depuis le package @nestjs/schedule.

Utilisez-le ensuite dans une classe comme suit. Supposons qu'une tâche cron ait été créée avec la déclaration suivante :


@Cron('* * 8 * * *', {
  name: 'notifications',
})
triggerNotifications() {}

Accédez à ce poste en utilisant les moyens suivants :


const job = this.schedulerRegistry.getCronJob('notifications');

job.stop();
console.log(job.lastDate());

La méthode getCronJob() retourne la tâche cron nommée. L'objet CronJob retourné possède les méthodes suivantes :

  • stop() - arrête une tâche dont l'exécution est programmée.
  • start() - redémarre une tâche qui a été arrêtée.
  • setTime(time: CronTime) - arrête une tâche, fixe une nouvelle heure pour celle-ci, puis la démarre
  • lastDate() - renvoie une représentation DateTime de la date à laquelle la dernière exécution d'une tâche a eu lieu.
  • nextDate() - renvoie une représentation DateTime de la date à laquelle la prochaine exécution d'une tâche est programmée.
  • nextDates(count: number) - Fournit un tableau (de taille count) de représentations DateTime pour la prochaine série de dates qui déclencheront l'exécution de la tâche. La valeur par défaut de count est 0, ce qui renvoie un tableau vide.
Astuce Utilisez toJSDate() sur les objets DateTime pour les convertir en une date JavaScript équivalente à cette DateTime.

Créez un nouveau job cron dynamiquement en utilisant la méthode SchedulerRegistry#addCronJob, comme suit :


addCronJob(name: string, seconds: string) {
  const job = new CronJob(`${seconds} * * * * *`, () => {
    this.logger.warn(`il est temps (${seconds}) pour la tâche ${name} d'être exécutée !`);
  });

  this.schedulerRegistry.addCronJob(name, job);
  job.start();

  this.logger.warn(
    `tâche ${name} ajoutée pour chaque minute à ${seconds} secondes !`,
  );
}

Dans ce code nous utilisons l'objet CronJob du package cron pour créer la tâche cron. Le constructeur CronJob prend un motif cron (tout comme le décorateur@Cron()) comme premier argument, et un callback à 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.

Attention N'oubliez pas d'injecter le SchedulerRegistry avant d'y accéder. Importez CronJob depuis le package cron.

Supprimez une tâche cron nommée en utilisant la méthode SchedulerRegistry#deleteCronJob, comme suit :


deleteCron(name: string) {
  this.schedulerRegistry.deleteCronJob(name);
  this.logger.warn(`tâche ${name} supprimée !`);
}

Listez tous les jobs cron 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 = 'erreur : la date de la prochaine exécution est dépassée !';
    }
    this.logger.log(`tâche : ${key} -> prochaine : ${next}`);
  });
}

La méthode getCronJobs() retourne une map. Dans ce code, nous itérons sur la carte et essayons d'accéder à la méthode nextDate() de chaque CronJob. Dans l'API CronJob, si un job a déjà été exécuté et n'a pas de date d'exécution future, une exception est levée.

Intervalles dynamiques#

Obtenez une référence à un intervalle avec la méthode SchedulerRegistry#getInterval. Comme ci-dessus, injectez SchedulerRegistry en utilisant l'injection de constructeur standard :


constructor(private schedulerRegistry: SchedulerRegistry) {}

Et utilisez-la comme suit :


const interval = this.schedulerRegistry.getInterval('notifications');
clearInterval(interval);

Créez un nouvel intervalle dynamiquement en utilisant la méthode SchedulerRegistry#addInterval, comme suit :


addInterval(name: string, milliseconds: number) {
  const callback = () => {
    this.logger.warn(`Intervalle ${name} exécuté à (${milliseconds}) !`);
  };

  const interval = setInterval(callback, milliseconds);
  this.schedulerRegistry.addInterval(name, interval);
}

Dans ce code, nous créons un intervalle JavaScript standard, puis nous 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(`Intervalle ${name} supprimé !`);
}

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(`Intervalle : ${key}`));
}

Délais dynamiques#

Obtenir une référence à un délai avec la méthode SchedulerRegistry#getTimeout. Comme ci-dessus, injectez SchedulerRegistry en utilisant l'injection de constructeur standard :


constructor(private readonly schedulerRegistry: SchedulerRegistry) {}

Et utilisez-le comme suit :


const timeout = this.schedulerRegistry.getTimeout('notifications');
clearTimeout(timeout);

Créez un nouveau timeout dynamiquement en utilisant la méthode SchedulerRegistry#addTimeout, comme suit :


addTimeout(name: string, milliseconds: number) {
  const callback = () => {
    this.logger.warn(`Délai ${name} exécuté après (${milliseconds}) !`);
  };

  const timeout = setTimeout(callback, milliseconds);
  this.schedulerRegistry.addTimeout(name, timeout);
}

Dans ce code, nous créons un timeout JavaScript standard, puis nous le passons à la méthode ScheduleRegistry#addTimeout. Cette méthode prend deux arguments : un nom pour le délai d'attente et le délai d'attente 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 de la manière suivante :


getTimeouts() {
  const timeouts = this.schedulerRegistry.getTimeouts();
  timeouts.forEach(key => this.logger.log(`Timeout: ${key}`));
}

Exemple#

Un exemple pratique est disponible ici.

Soutenez-nous

Nest est un projet open source sous licence MIT. Il peut se développer grâce au soutien de ces personnes formidables. Si vous souhaitez les rejoindre, apprenez-en plus ici.

Sponsors Principaux

Trilon LogoMarblism LogoMojam LogoAmplication Logo

Sponsors / Partenaires

Devenir un sponsor