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 :
- Navigation utilisateur : Un utilisateur initie une requĂȘte de recherche, dĂ©clenchant une requĂȘte API. S'il navigue rapidement vers une autre page avant que la requĂȘte ne soit terminĂ©e, la requĂȘte d'origine devient non pertinente et doit ĂȘtre annulĂ©e pour Ă©viter un trafic rĂ©seau inutile et des effets secondaires potentiels.
- Gestion des délais d'attente : Vous définissez un délai d'attente pour une opération asynchrone. Si l'opération se termine avant l'expiration du délai, vous devez annuler le délai pour éviter une exécution de code redondante.
- DĂ©montage de composant : Dans les frameworks front-end comme React ou Vue.js, les composants effectuent souvent des requĂȘtes asynchrones. Lorsqu'un composant est dĂ©montĂ©, toute requĂȘte en cours associĂ©e Ă ce composant doit ĂȘtre annulĂ©e pour Ă©viter les fuites de mĂ©moire et les erreurs causĂ©es par la mise Ă jour de composants dĂ©montĂ©s.
- Contraintes de ressources : Dans les environnements aux ressources limitées (par exemple, appareils mobiles, systÚmes embarqués), l'annulation des opérations inutiles peut libérer des ressources précieuses et améliorer les performances. Par exemple, annuler le téléchargement d'une grande image si l'utilisateur fait défiler cette section de la page.
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 :
- AbortController : Cet objet gÚre le signal d'annulation. Il possÚde une seule méthode,
abort(), qui est utilisĂ©e pour signaler une demande d'annulation. - AbortSignal : Cet objet reprĂ©sente le signal indiquant qu'une opĂ©ration doit ĂȘtre abandonnĂ©e. Il est associĂ© Ă un
AbortControlleret est passĂ© Ă l'opĂ©ration asynchrone qui doit ĂȘtre annulable.
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 :
- Nous créons une instance
AbortController. - Nous obtenons l'
AbortSignalassocié à partir ducontroller. - Nous passons le
signalaux optionsfetch. - Si nous devons annuler la requĂȘte, nous appelons
controller.abort(). - Dans le bloc
.catch(), nous vĂ©rifions si l'erreur est unAbortError. 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 :
- Vérifiez le nom de l'erreur : Vérifiez toujours si
error.name === 'AbortError'pour vous assurer que vous traitez le bon type d'erreur. - Retournez une valeur par défaut ou relancez : Selon la logique de votre application, vous pourriez vouloir retourner une valeur par défaut (par exemple,
null) ou relancer l'erreur pour qu'elle soit traitée plus haut dans la pile d'appels. - Nettoyez les ressources : Si l'opération asynchrone a alloué des ressources (par exemple, des minuteurs, des écouteurs d'événements), nettoyez-les dans le gestionnaire d'
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 :
- La fonction
cancellableTimeoutprend un callback, un délai et unAbortSignalcomme arguments. - Elle configure un
setTimeoutet stocke l'ID du minuteur. - Elle ajoute un écouteur d'événements à l'
AbortSignalqui écoute l'événementabort. - Lorsque l'événement
abortest 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 :
- Nous passons le
signalcomme option à la méthodeaddEventListener. - 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 :
- Nous créons un
AbortControllerdans le hookuseEffect. - Nous passons le
signalĂ la requĂȘtefetch. - Nous retournons une fonction de nettoyage du hook
useEffect. Cette fonction sera appelée lors du démontage du composant. - à 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
- Amélioration des performances : L'annulation des opérations inutiles peut réduire le trafic réseau, l'utilisation du CPU et la consommation de mémoire, ce qui améliore les performances, en particulier sur les appareils aux ressources limitées.
- Code plus propre :
AbortControllerfournit un moyen standardisé et élégant de gérer l'annulation, rendant votre code plus lisible et maintenable. - Prévention des fuites de mémoire : L'annulation des opérations asynchrones associées aux composants démontés évite les fuites de mémoire et les erreurs causées par la mise à jour de composants non montés.
- Meilleure expĂ©rience utilisateur : L'annulation des requĂȘtes non pertinentes peut amĂ©liorer l'expĂ©rience utilisateur en empĂȘchant l'affichage d'informations obsolĂštes et en rĂ©duisant la latence perçue.
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.