Français

Apprenez à utiliser l'AbortController de JavaScript pour annuler efficacement les opérations asynchrones (fetch, timers, etc.) pour un code plus propre et performant.

JavaScript AbortController : Maîtriser l'annulation des opérations asynchrones

Dans le développement web moderne, les opérations asynchrones sont omniprésentes. Récupérer des données d'API, définir des minuteries et gérer les interactions utilisateur impliquent souvent du code qui s'exécute indépendamment et potentiellement pendant une durée prolongée. Cependant, il existe des scénarios où vous devez annuler ces opérations avant qu'elles ne soient terminées. C'est là que l'interface AbortController en JavaScript vient à la rescousse. Elle fournit un moyen propre et efficace de signaler des demandes d'annulation aux opérations DOM et à d'autres tâches asynchrones.

Comprendre le besoin d'annulation

Avant de plonger dans les détails techniques, comprenons pourquoi l'annulation des opérations asynchrones est importante. Considérez ces scénarios courants :

Introduction à AbortController et AbortSignal

L'interface AbortController est conçue pour résoudre le problème de l'annulation des opérations asynchrones. Elle se compose de deux éléments clés :

Utilisation de base : Annuler les requêtes Fetch

Commençons par un exemple simple d'annulation d'une requête fetch :


const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/data', { signal })
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Data:', data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// Pour annuler la requête fetch :
controller.abort();

Explication :

  1. Nous créons une instance AbortController.
  2. Nous obtenons l'AbortSignal associé à partir du controller.
  3. Nous passons le signal aux options fetch.
  4. Si nous devons annuler la requête, nous appelons controller.abort().
  5. Dans le bloc .catch(), nous vérifions si l'erreur est un AbortError. Si c'est le cas, nous savons que la requête a été annulée.

Gestion de AbortError

Lorsque controller.abort() est appelé, la requête fetch sera rejetée avec un AbortError. Il est crucial de gérer cette erreur de manière appropriée dans votre code. Ne pas le faire peut entraîner des rejets de promesse non gérés et un comportement inattendu.

Voici un exemple plus robuste avec gestion des erreurs :


const controller = new AbortController();
const signal = controller.signal;

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data', { signal });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
      return null; // Ou lancez l'erreur pour être traitée plus loin
    } else {
      console.error('Fetch error:', error);
      throw error; // Relancez l'erreur pour être traitée plus loin
    }
  }
}

fetchData();

// Pour annuler la requête fetch :
controller.abort();

Bonnes pratiques pour la gestion de AbortError :

Annuler les minuteurs avec AbortSignal

L'AbortSignal peut également être utilisé pour annuler les minuteurs créés avec setTimeout ou setInterval. Cela nécessite un peu plus de travail manuel, car les fonctions de minuterie intégrées ne prennent pas directement en charge AbortSignal. Vous devez créer une fonction personnalisée qui écoute le signal d'abandon et efface le minuteur lorsqu'il est déclenché.


function cancellableTimeout(callback, delay, signal) {
  let timeoutId;

  const timeoutPromise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(() => {
      resolve(callback());
    }, delay);

    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new Error('Timeout Aborted'));
    });
  });

  return timeoutPromise;
}

const controller = new AbortController();
const signal = controller.signal;


cancellableTimeout(() => {
  console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));

// Pour annuler le délai d'attente :
controller.abort();

Explication :

  1. La fonction cancellableTimeout prend un callback, un délai et un AbortSignal comme arguments.
  2. Elle configure un setTimeout et stocke l'ID du minuteur.
  3. Elle ajoute un écouteur d'événements à l'AbortSignal qui écoute l'événement abort.
  4. Lorsque l'événement abort est déclenché, l'écouteur d'événements efface le minuteur et rejette la promesse.

Annuler les écouteurs d'événements

Similaire aux minuteurs, vous pouvez utiliser AbortSignal pour annuler les écouteurs d'événements. C'est particulièrement utile lorsque vous souhaitez supprimer les écouteurs d'événements associés à un composant qui est en cours de démontage.


const controller = new AbortController();
const signal = controller.signal;

const button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log('Button clicked!');
}, { signal });

// Pour annuler l'écouteur d'événements :
controller.abort();

Explication :

  1. Nous passons le signal comme option à la méthode addEventListener.
  2. Lorsque controller.abort() est appelé, l'écouteur d'événements sera automatiquement supprimé.

AbortController dans les composants React

Dans React, vous pouvez utiliser AbortController pour annuler les opérations asynchrones lors du démontage d'un composant. Ceci est essentiel pour éviter les fuites de mémoire et les erreurs causées par la mise à jour de composants non montés. Voici un exemple utilisant le hook useEffect :


import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data', { signal });
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          console.error('Fetch error:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Annule la requête fetch lorsque le composant est démonté
    };
  }, []); // Le tableau de dépendances vide garantit que cet effet ne s'exécute qu'une seule fois au montage

  return (
    
{data ? (

Data: {JSON.stringify(data)}

) : (

Loading...

)}
); } export default MyComponent;

Explication :

  1. Nous créons un AbortController dans le hook useEffect.
  2. Nous passons le signal à la requête fetch.
  3. Nous retournons une fonction de nettoyage du hook useEffect. Cette fonction sera appelée lors du démontage du composant.
  4. À l'intérieur de la fonction de nettoyage, nous appelons controller.abort() pour annuler la requête fetch.

Cas d'utilisation avancés

Chaînage de AbortSignals

Parfois, vous voudrez peut-être chaîner plusieurs AbortSignal ensemble. Par exemple, vous pourriez avoir un composant parent qui doit annuler les opérations dans ses composants enfants. Vous pouvez y parvenir en créant un nouvel AbortController et en passant son signal aux composants parent et enfant.

Utilisation d'AbortController avec des bibliothèques tierces

Si vous utilisez une bibliothèque tierce qui ne prend pas directement en charge AbortSignal, vous devrez peut-être adapter votre code pour fonctionner avec le mécanisme d'annulation de la bibliothèque. Cela pourrait impliquer d'encapsuler les fonctions asynchrones de la bibliothèque dans vos propres fonctions qui gèrent l'AbortSignal.

Avantages de l'utilisation d'AbortController

Compatibilité des navigateurs

AbortController est largement pris en charge dans les navigateurs modernes, y compris Chrome, Firefox, Safari et Edge. Vous pouvez consulter le tableau de compatibilité sur les MDN Web Docs pour obtenir les informations les plus récentes.

Polyfills

Pour les anciens navigateurs qui ne prennent pas en charge nativement AbortController, vous pouvez utiliser un polyfill. Un polyfill est un morceau de code qui fournit la fonctionnalité d'une nouvelle fonctionnalité dans les anciens navigateurs. Il existe plusieurs polyfills AbortController disponibles en ligne.

Conclusion

L'interface AbortController est un outil puissant pour gérer les opérations asynchrones en JavaScript. En utilisant AbortController, vous pouvez écrire du code plus propre, plus performant et plus robuste qui gère l'annulation avec élégance. Que vous récupériez des données d'API, que vous définissiez des minuteurs ou que vous gériez des écouteurs d'événements, AbortController peut vous aider à améliorer la qualité globale de vos applications web.

Lectures complémentaires