AbortController : annuler un fetch devenu inutile
Annulez proprement une requête réseau quand l’utilisateur change de page, lance une nouvelle recherche ou qu’un délai maximal est dépassé.
Où en êtes-vous avec cette notion ?
Une indication personnelle, enregistrée uniquement dans ce navigateur.
À lire ensuite
Le format café
L’essentiel à comprendre, le temps d’un café.
Envie de creuser ? Un cours plus complet vous attend juste après, à déplier sans quitter cette page.
L’idée intuitive
Imaginez une recherche instantanée. Vous tapez « rea », puis immédiatement « react ». Deux requêtes partent :
"rea" ───────────────► réponse lente
"react" ───────► réponse rapide
Si la première réponse arrive en dernier, elle peut remplacer les résultats plus récents. Et même si vous ignorez son résultat, le navigateur continue à télécharger des données devenues inutiles.
AbortController sert à dire : « cette opération ne m’intéresse plus, arrête-la si elle est encore en cours ».
Le duo Controller + Signal
Un contrôleur fabrique un signal. Vous donnez le signal à fetch(), puis vous gardez le contrôleur pour pouvoir annuler plus tard.
const controller = new AbortController();
fetch('/api/search?q=react', {
signal: controller.signal,
});
// Plus tard, si la requête n'est plus utile :
controller.abort();
Le signal ne « tue » pas lui-même la requête. Il transmet l’information d’annulation à l’API qui l’écoute.
Une recherche qui remplace la précédente
let controller;
async function rechercher(query) {
controller?.abort();
controller = new AbortController();
try {
const response = await fetch(
`/api/search?q=${encodeURIComponent(query)}`,
{ signal: controller.signal },
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
return null;
}
throw error;
}
}
À chaque nouvelle recherche, l’ancienne est annulée avant d’en lancer une autre. Un abandon normal n’est donc pas affiché comme une panne réseau.
Un délai maximal sans minuteur manuel
Pour une opération qui ne doit pas attendre indéfiniment :
const response = await fetch('/api/status', {
signal: AbortSignal.timeout(5000),
});
Après cinq secondes, le signal est annulé. Cette fois, le rejet utilise une DOMException nommée TimeoutError, ce qui permet de distinguer un délai dépassé d’une annulation volontaire.
Le piège important
Annuler fetch() n’annule pas le monde côté serveur. Si une requête a déjà déclenché un paiement, un envoi d’e-mail ou une écriture en base, couper l’attente du navigateur ne rembobine pas cet effet.
Pour les écritures importantes, l’annulation côté client complète donc — mais ne remplace jamais — les transactions, l’idempotence et les règles métier du serveur.
Fiche mémo
new AbortController()crée un contrôleur et sonsignal.- Passez
signalà l’opération annulable, par exemplefetch(). controller.abort()signale que l’opération n’est plus souhaitée.- Un signal déjà annulé ne se réutilise pas pour une nouvelle requête : créez un nouveau contrôleur.
AbortSignal.timeout(ms)fournit un délai maximal très lisible.
L’idée à emporter : une requête asynchrone peut être parfaitement valide et pourtant ne plus être utile ; l’annulation évite alors du travail inutile et des réponses périmées.
Et si on allait plus loin ?
Le café vous a donné les repères. Prenez maintenant le temps de comprendre les mécanismes et de pratiquer, si vous le souhaitez.
Aller plus loinConcevoir une annulation propre, de fetch à ReactDévelopper le coursReplier le cours
Ce que vous saurez faire
Comprendre pourquoi une Promise n’est pas elle-même « annulable », transmettre un même signal à plusieurs opérations, utiliser l’annulation dans une fonction maison et nettoyer correctement un effet React.
1. AbortController est un protocole de coopération
Une Promise représente un résultat futur ; elle ne possède pas de bouton d’arrêt universel. Le DOM Standard sépare donc les rôles :
AbortController
│ abort()
▼
AbortSignal ─────────► fetch / stream / fonction compatible
L’opération décide d’écouter le signal. Lorsqu’elle le fait, elle peut interrompre son travail et rejeter sa Promise avec la raison d’annulation du signal.
Cette séparation est utile parce qu’un même signal peut coordonner plusieurs opérations :
const controller = new AbortController();
const { signal } = controller;
const profil = fetch('/api/profile', { signal });
const commandes = fetch('/api/orders', { signal });
// Quitter l'écran peut arrêter les deux demandes.
controller.abort();
Référence : DOM Standard — Aborting ongoing activities.
2. Un signal est à usage unique
Après abort(), signal.aborted reste vrai. Toute nouvelle opération utilisant ce même signal voit immédiatement qu’il est déjà annulé.
const controller = new AbortController();
controller.abort();
console.log(controller.signal.aborted); // true
// Cette requête part avec un signal déjà annulé.
fetch('/api/data', { signal: controller.signal })
.catch((error) => console.log(error.name)); // AbortError
Le bon modèle pour une recherche successive est donc un contrôleur par tentative, pas un contrôleur global que l’on « réarme ».
Référence : MDN — AbortSignal.
3. Propager l’annulation dans votre propre fonction
Vos fonctions asynchrones peuvent accepter un AbortSignal exactement comme fetch().
async function chargerProfil(id, { signal } = {}) {
signal?.throwIfAborted();
const response = await fetch(`/api/users/${id}`, { signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
throwIfAborted() permet d’arrêter immédiatement une fonction si l’appelant a déjà annulé l’opération. Vous conservez ainsi une seule chaîne d’annulation du composant jusqu’au réseau.
Pour un traitement maison qui ne s’appuie pas sur une API déjà compatible, écoutez l’événement abort, libérez vos ressources et rejetez avec signal.reason. Retirez ensuite l’écouteur, ou utilisez un écouteur à usage unique, pour ne pas conserver inutilement des références.
4. Le nettoyage naturel dans React
Une utilisation fréquente apparaît lorsqu’un composant charge des données selon une propriété. Si cette propriété change ou si le composant disparaît, l’ancienne requête ne sert plus.
useEffect(() => {
const controller = new AbortController();
async function charger() {
try {
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') return;
setError(error);
}
}
charger();
return () => controller.abort();
}, [userId]);
Le nettoyage de l’effet annule l’ancienne requête avant que l’effet suivant ne démarre, ou lorsque le composant est retiré. Cela évite de conserver une demande réseau dont le résultat n’a plus de destinataire pertinent.
5. Timeout et raison d’annulation
AbortSignal.timeout(5000) produit un signal qui s’annule automatiquement après cinq secondes. Le DOM Standard définit alors une raison de type TimeoutError.
try {
await fetch('/api/report', {
signal: AbortSignal.timeout(5000),
});
} catch (error) {
if (error instanceof DOMException && error.name === 'TimeoutError') {
console.warn('Le serveur a dépassé le délai prévu.');
} else {
throw error;
}
}
Une annulation volontaire et un timeout peuvent donc conduire à des messages différents. Ne transformez pas toutes les erreurs en « requête annulée » : une vraie panne réseau ou une erreur de parsing mérite toujours d’être visible.
Pièges importants
- Annuler n’est pas rollback. Le serveur peut avoir reçu et traité la requête avant l’annulation du client.
- Ne réutilisez pas un signal annulé. Il reste définitivement dans cet état.
- N’avalez pas toutes les erreurs. Ignorez uniquement l’annulation que votre interface considère comme normale ; propagez les autres.
- Le corps de réponse aussi peut être interrompu. Une annulation peut survenir après les en-têtes et pendant la lecture de
response.json()ou d’un flux. - L’annulation doit se propager. Si votre fonction appelle une autre API asynchrone sans lui transmettre le signal, seule une partie du travail s’arrête.
Mémo final
Pensez AbortSignal comme un petit canal de contrôle parallèle à vos données : l’appelant dit « je n’en ai plus besoin », et chaque couche compatible transmet cette décision jusqu’au travail réellement en cours. Cette mécanique ne remplace aucune garantie serveur ; elle rend surtout l’interface plus propre, plus économe et moins vulnérable aux résultats devenus obsolètes.
Vous pouvez aussi vous arrêter ici. L’approfondissement est facultatif.
Les repères Cours Café
Pour aller à la source
Documentation de référence. Vérification éditoriale encore à effectuer.
Gardez une trace de cette idée.
Favoris, notes et progression seront disponibles après connexion du stockage distant.