🌐 AIæœçŽą & 代理 䞻饔
11 juillet 2023

Fetch: Abort

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 devient true.

Generally, we have two parties in the process:

  1. The one that performs a cancelable operation, it sets a listener on controller.signal.
  2. 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Ă©nement abort sur sa propriĂ©tĂ© signal lorsque la mĂ©thode abort() est appelĂ©e (et dĂ©finit Ă©galement signal.aborted sur true).
  • fetch s’intĂšgre avec lui : nous passons la propriĂ©tĂ© signal comme option, puis fetch l’écoute, il devient donc possible d’annuler le fetch.
  • Nous pouvons utiliser AbortController dans notre code. L’interaction “appeler abort()” → â€œĂ©couter l’évĂ©nement abort” est simple et universelle. Nous pouvons l’utiliser mĂȘme sans fetch.
Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)