Comme nous le savons, fetch renvoie une promesse. Et JavaScript n’a généralement aucun concept d’“annulation” d’une promesse. Alors, comment pouvons-nous annuler un fetch en cours ? Par exemple. si les actions de l’utilisateur sur notre site indiquent que le fetch n’est plus nécessaire.
Il existe un objet intégré spécial dédié : AbortController, qui peut être utilisé pour abandonner non seulement un fetch, mais aussi d’autres tâches asynchrones.
L’utilisation est assez simple :
L’objet AbortController
Créez un contrôleur :
let controller = new AbortController();
Un contrôleur est un objet extrêmement simple.
- Il a une seule méthode
abort(), - Et une seule propriété
signalqui permet de définir des écouteurs d’événements dessus.
Quand abort() est appelé :
controller.signalémet l’événement"abort".- La propriété
controller.signal.aborteddevienttrue.
Generally, we have two parties in the process:
- The one that performs a cancelable operation, it sets a listener on
controller.signal. - The one that cancels: it calls
controller.abort()when needed.
Voici l’exemple complet (sans fetch encore) :
let controller = new AbortController();
let signal = controller.signal;
// The party that performs a cancelable operation
// gets the "signal" object
// and sets the listener to trigger when controller.abort() is called
signal.addEventListener('abort', () => alert("abort!"));
// The other party, that cancels (at any point later):
controller.abort(); // abort!
// L'événement se déclenche et signal.aborted devient vrai
alert(signal.aborted); // true
As we can see, AbortController is just a mean to pass abort events when abort() is called on it.
Nous pourrions implémenter le même type d’écoute d’événement dans notre code par nous-mêmes, sans objet AbortController.
Mais ce qui est précieux, c’est que fetch sait comment travailler avec l’objet AbortController, il est intégré avec lui.
Utilisation avec fetch
Pour pouvoir annuler fetch, passez la propriété signal d’un AbortController comme une option fetch :
let controller = new AbortController();
fetch(url, {
signal: controller.signal
});
La méthode fetch sait comment travailler avec AbortController. Il écoutera les événements abort sur signal.
Maintenant, pour abandonner, appelons controller.abort() :
controller.abort();
Nous avons terminé: fetch récupère l’événement de signal et abandonne la requête.
Lorsqu’une extraction est abandonnée, sa promesse est rejetée avec une erreur AbortError, nous devons donc la gérer, par ex. dans try..catch.
Voici l’exemple complet avec fetch abandonné après 1 seconde :
// abandonner en 1 seconde
let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
try {
let response = await fetch('/article/fetch-abort/demo/hang', {
signal: controller.signal
});
} catch(err) {
if (err.name == 'AbortError') { // gère abort()
alert("Aborted!");
} else {
throw err;
}
}
AbortController est évolutif
AbortController est évolutif, il permet d’annuler plusieurs récupérations à la fois.
Voici une esquisse de code qui récupère de nombreuses urls en parallèle et utilise un seul contrôleur pour toutes les abandonner:
let urls = [...]; // une liste d'urls à récupérer en parallèle
let controller = new AbortController();
// an array of fetch promises
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));
let results = await Promise.all(fetchJobs);
// si controller.abort() est appelée d'ailleurs,
// elle interrompt tous les fetches
Si nous avons nos propres tâches asynchrones, différentes de fetch, nous pouvons utiliser un seul AbortController pour les arrêter, avec des fetches.
Nous avons juste besoin d’écouter son événement abort dans nos tâches :
let urls = [...];
let controller = new AbortController();
let ourJob = new Promise((resolve, reject) => { // notre tâche
...
controller.signal.addEventListener('abort', reject);
});
let fetchJobs = urls.map(url => fetch(url, { // fetches
signal: controller.signal
}));
// Wait for fetches and our task in parallel
let results = await Promise.all([...fetchJobs, ourJob]);
// si controller.abort() est appelée d'ailleurs,
// elle interrompt tous les fetches et ourJob
Résumé
AbortControllerest un objet simple qui génère un événementabortsur sa propriétésignallorsque la méthodeabort()est appelée (et définit égalementsignal.abortedsurtrue).fetchs’intègre avec lui : nous passons la propriétésignalcomme option, puisfetchl’écoute, il devient donc possible d’annuler lefetch.- Nous pouvons utiliser
AbortControllerdans notre code. L’interaction “appelerabort()” → “écouter l’événementabort” est simple et universelle. Nous pouvons l’utiliser même sansfetch.
Commentaires
<code>, pour plusieurs lignes – enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen…)