Skip to content
On this page

Comment utiliser exhaustMap sur RxJS ?

exhaustMap est un opérateur de RxJS utilisé pour la gestion des flux de données asynchrones. Il est souvent utilisé lorsqu'on veut ignorer les nouvelles émissions d'un observable jusqu'à ce que l'observable en cours se termine.

Voici une explication simple :

javascript
import { interval, timer, take, exhaustMap, map } from 'rxjs';

const source = interval(50).pipe(take(5)); 
const result = source.pipe(
  exhaustMap(value => timer(20))
);

result.subscribe(
  message => console.log(message),
  error => console.error('Erreur :', error),
  () => console.log('Toutes les opérations sont terminées')
);

Description du code

  1. Création de l'observable source :

    • interval(50) crée un observable qui émet des valeurs séquentielles (0, 1, 2, 3, 4, ...) toutes les 50 millisecondes.
    • take(5) limite cette émission à 5 valeurs (0, 1, 2, 3, 4).
  2. Utilisation de exhaustMap :

    • exhaustMap est utilisé pour transformer chaque valeur émise par source en un nouvel observable timer(20).
    • timer(20) crée un observable qui émet une seule valeur après un délai de 20 millisecondes.
  3. Souscription :

    • On souscrit à result pour afficher chaque valeur une fois que le timer de 20 millisecondes pour cette valeur a terminé.
    • Une fois que toutes les valeurs ont été traitées, le message "Toutes les opérations sont terminées" est affiché.

Intérêt de exhaustMap

L'intérêt principal de exhaustMap dans ce contexte est d'assurer que chaque nouvelle valeur émise par l'observable source est ignorée tant que l'inner observable précédent n'a pas terminé son émission.

Points clés de exhaustMap :

  • Exécution exclusive : exhaustMap garantit que si une nouvelle valeur est émise par source pendant qu'un inner observable est en cours d'exécution, cette nouvelle valeur sera ignorée. Cela assure qu'aucun observable inner ne sera exécuté en parallèle.

  • Ignorer les valeurs rapides : Si les valeurs émises par source sont plus rapides que la durée de l'inner observable (timer(20)), les valeurs supplémentaires seront ignorées jusqu'à ce que l'inner observable en cours soit terminé.

Fonctionnement avec timer(20)

  • Émission rapide : Ici, chaque valeur de source est transformée en un timer qui attend 20 millisecondes. Comme interval(50) émet toutes les 50 millisecondes, chaque valeur est prise en compte, car le délai de 20 millisecondes est plus court que l'intervalle de 50 millisecondes.
  • Aucun événement ignoré : Puisque 20 ms < 50 ms, l'inner observable a toujours le temps de terminer avant que le source émette une nouvelle valeur. Donc, aucun événement n'est ignoré dans ce cas spécifique.

Fonctionnement avec un timer plus grand

Si on modifie le timer pour avoir un délai plus long, par exemple timer(100), le comportement change :

javascript
import { interval, timer } from 'rxjs';
import { take, exhaustMap, map } from 'rxjs/operators';

const source = interval(50).pipe(take(5)); 
const result = source.pipe(
  exhaustMap(value => timer(100))
);

result.subscribe(
  message => console.log(message),
  error => console.error('Erreur :', error),
  () => console.log('Toutes les opérations sont terminées')
);
  • Émission plus lente : Chaque valeur de source est transformée en un timer qui attend 100 millisecondes. Comme interval(50) émet toutes les 50 millisecondes, le délai de 100 millisecondes est plus long que l'intervalle de 50 millisecondes.
  • Événements ignorés : Puisque 100 ms > 50 ms, le timer de l'inner observable n'a pas le temps de se terminer avant que source émette une nouvelle valeur. Par conséquent, toutes les valeurs suivantes de source seront ignorées tant que l'inner observable n'aura pas terminé.

# Exemple avec des clics rapides

Pour montrer un exemple où des clics rapides émettent des requêtes HTTP mais où nous voulons ignorer les clics supplémentaires tant que la requête en cours n'est pas terminée, nous pouvons utiliser exhaustMap.

Voici comment cela peut être fait :

javascript
import { fromEvent, exhaustMap, ajax } from 'rxjs';

// Sélectionnez le bouton HTML sur lequel les clics seront écoutés
const button = document.getElementById('myButton');

// Observable qui écoute les clics sur le bouton
const clicks = fromEvent(button, 'click');

// Fonction qui retourne un observable représentant une requête HTTP
const makeRequest = () => ajax.getJSON('https://jsonplaceholder.typicode.com/todos/1');

// Utilisation de exhaustMap pour gérer les requêtes de manière à ignorer les clics rapides
const result = clicks.pipe(
  exhaustMap(() => makeRequest())
);

// Souscription au résultat pour afficher les données
result.subscribe(
  data => console.log('Données reçues :', data),
  error => console.error('Erreur :', error),
  () => console.log('Requête terminée')
);

Fonctionnement

  1. L'utilisateur clique sur le bouton, déclenchant un événement de clic.
  2. exhaustMap capture cet événement et lance makeRequest pour effectuer une requête HTTP.
  3. Si l'utilisateur clique à nouveau avant que la requête en cours ne soit terminée, ces clics sont ignorés.
  4. Une fois la requête terminée, si un nouveau clic se produit, une nouvelle requête est lancée.

Cet exemple illustre parfaitement comment exhaustMap peut être utilisé pour gérer des opérations asynchrones de manière exclusive, garantissant qu'une nouvelle opération n'est lancée que lorsque la précédente est terminée, même dans le cas de clics rapides successifs.

Résumé

  • Ignorer les clics rapides : Si l'utilisateur clique plusieurs fois rapidement, exhaustMap ignore les clics supplémentaires tant que la requête en cours n'est pas terminée.
  • Exécution exclusive : Cela garantit qu'une seule requête HTTP est en cours à la fois, évitant les surcharges de serveur ou les requêtes multiples inutiles.

cas d'utilisation

TitreDescription
Gestion de clics rapidesIgnorer les clics supplémentaires tant que la requête HTTP en cours n'est pas terminée, empêchant les surcharges de requêtes.
Détection de mouvementIgnorer les nouvelles valeurs de détection de mouvement tant que le traitement précédent n'est pas terminé, pour éviter les traitements redondants.
Suivi de position GPSIgnorer les nouvelles valeurs de position GPS tant que l'actualisation précédente n'est pas terminée, assurant une gestion ordonnée des données de position.
Envoi de messages de chatEmpêcher l'envoi de nouveaux messages tant que le message précédent n'a pas été confirmé par le serveur, assurant une livraison ordonnée.
Validation de formulaireIgnorer les nouvelles tentatives de validation tant que la validation en cours n'est pas terminée, empêchant les requêtes multiples de validation.
Chargement de donnéesEmpêcher les nouvelles requêtes de chargement de données tant que le chargement précédent n'est pas terminé, pour éviter les conflits de données.
Lecture de capteurs IoTIgnorer les nouvelles lectures de capteurs tant que la lecture précédente n'a pas été traitée, assurant une gestion ordonnée des données des capteurs.
Mises à jour de configurationIgnorer les nouvelles tentatives de mise à jour de configuration tant que la mise à jour précédente n'est pas terminée, assurant la cohérence des configurations.
Téléchargement de fichiersEmpêcher les nouveaux téléchargements de fichiers tant que le téléchargement précédent n'est pas terminé, évitant les conflits de ressources.
Gestion des notificationsIgnorer les nouvelles notifications tant que la notification en cours n'a pas été traitée, pour éviter les surcharges de notifications.