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é
signal
qui 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.aborted
devienttrue
.
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é
AbortController
est un objet simple qui génère un événementabort
sur sa propriétésignal
lorsque la méthodeabort()
est appelée (et définit égalementsignal.aborted
surtrue
).fetch
s’intègre avec lui : nous passons la propriétésignal
comme option, puisfetch
l’écoute, il devient donc possible d’annuler lefetch
.- Nous pouvons utiliser
AbortController
dans 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…)