AbortSignal
Baseline
Widely available
*
Cette fonctionnalité est bien établie et fonctionne sur de nombreux appareils et versions de navigateurs. Elle est disponible sur tous les navigateurs depuis avril 2018.
* Certaines parties de cette fonctionnalité peuvent bénéficier de prise en charge variables.
Note : Cette fonctionnalité est disponible via les Web Workers.
L'interface AbortSignal représente un objet signal qui permet de communiquer avec une opération asynchrone (comme une requête fetch) et de l'annuler si nécessaire via un objet AbortController.
Propriétés d'instance
Hérite également des propriétés de son interface parente, EventTarget.
AbortSignal.abortedLecture seule-
Un booléen qui indique si la ou les requêtes avec lesquelles le signal communique sont annulées (
true) ou non (false). AbortSignal.reasonLecture seule-
Une valeur JavaScript fournissant la raison de l'annulation, une fois le signal annulé.
Méthodes statiques
Hérite également des méthodes de son interface parente, EventTarget.
AbortSignal.abort()-
Retourne une instance d'
AbortSignaldéjà à l'état annulé. AbortSignal.any()-
Retourne un
AbortSignalqui s'annule dès qu'un des signaux donnés est annulé. AbortSignal.timeout()-
Retourne une instance d'
AbortSignalqui s'annulera automatiquement après un temps défini.
Méthodes d'instance
Hérite également des méthodes de son interface parente, EventTarget.
AbortSignal.throwIfAborted()-
Lève une exception avec la
reasondu signal si celui-ci a été annulé ; sinon, ne fait rien.
Évènements
Hérite également des évènements de son interface parente, EventTarget.
Écoutez cet évènement avec addEventListener() ou en assignant un gestionnaire à la propriété oneventname de cette interface.
abort-
Déclenché lorsque les opérations asynchrones avec lesquelles le signal communique sont annulées. Également disponible via la propriété
onabort.
Exemples
>Annuler une opération fetch à l'aide d'un signal explicite
L'extrait suivant montre comment utiliser un signal pour annuler le téléchargement d'une vidéo avec l'API Fetch.
On commence par définir une variable pour notre AbortController.
Avant chaque requête fetch, on crée un nouveau contrôleur avec le constructeur AbortController(), puis on récupère une référence à son objet AbortController.signal AbortSignal associé via la propriété.
Note :
Un AbortSignal ne peut être utilisé qu'une seule fois. Une fois annulé, tout appel à fetch utilisant ce même signal sera immédiatement rejeté.
Lorsque la requête fetch est lancée, on passe le AbortSignal en option dans l'objet d'options de la requête (voir { signal } ci-dessous). Cela associe le signal et le contrôleur à la requête fetch et permet de l'annuler en appelant AbortController.abort(), comme illustré dans le second gestionnaire d'évènement.
Quand abort() est appelé, la promesse fetch() est rejetée avec un DOMException nommé AbortError.
let controller;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
if (controller) {
controller.abort();
console.log("Téléchargement annulé");
}
});
async function fetchVideo() {
controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(url, { signal });
console.log("Téléchargement terminé", response);
// traite la réponse plus en détail
} catch (err) {
console.error(`Erreur de téléchargement : ${err.message}`);
}
}
Si la requête est annulée après l'exécution de l'appel à fetch() mais avant que le corps de la réponse n'ait été lu, toute tentative de lecture du corps de la réponse rejettera avec une exception AbortError.
async function get() {
const controller = new AbortController();
const request = new Request("https://exemple.org/get", {
signal: controller.signal,
});
const response = await fetch(request);
controller.abort();
// La ligne suivante génère une exception `AbortError`
const text = await response.text();
console.log(text);
}
Vous pouvez trouver un exemple complet et fonctionnel sur GitHub (angl.) ; vous pouvez aussi le voir en ligne (angl.).
Annuler une opération fetch avec un délai d'expiration
Si vous devez annuler l'opération après un délai, vous pouvez utiliser la méthode statique AbortSignal.timeout().
Cela retourne un AbortSignal qui s'annulera automatiquement après un certain nombre de millisecondes.
L'extrait ci-dessous montre comment réussir à télécharger un fichier ou gérer une erreur de délai d'attente après 5 secondes.
Notez qu'en cas de délai, la promesse fetch() est rejetée avec un DOMException de type TimeoutError.
Cela permet de différencier les délais (pour lesquels une notification utilisateur·ice est probablement requise) des annulations utilisateur·ice.
const url = "video.mp4";
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
const result = await res.blob();
// …
} catch (err) {
if (err.name === "TimeoutError") {
console.error(
"Délai d'attente : il a fallu plus de 5 secondes pour obtenir le résultat !",
);
} else if (err.name === "AbortError") {
console.error(
"La récupération a été annulée par une action utilisateur (bouton d'arrêt du navigateur, fermeture de l'onglet, etc.).",
);
} else {
// Une erreur réseau, ou un autre problème.
console.error(`Erreur : type : ${err.name}, message : ${err.message}`);
}
}
Annuler un fetch avec délai ou annulation explicite
Si vous souhaitez annuler à partir de plusieurs signaux, vous pouvez utiliser AbortSignal.any() pour les combiner en un seul signal. L'exemple suivant montre cela avec fetch :
try {
const controller = new AbortController();
const timeoutSignal = AbortSignal.timeout(5000);
const res = await fetch(url, {
// Cela annule le fetch lorsque l'un ou l'autre des signaux est annulé
signal: AbortSignal.any([controller.signal, timeoutSignal]),
});
const body = await res.json();
} catch (e) {
if (e.name === "AbortError") {
// Notifie l'utilisateur·ice de l'annulation.
} else if (e.name === "TimeoutError") {
// Notifie l'utilisateur·ice du délai d'attente
} else {
// Une erreur réseau, ou un autre problème.
console.log(`Type : ${e.name}, Message : ${e.message}`);
}
}
Note :
Contrairement à l'utilisation de AbortSignal.timeout(), il n'est pas possible de savoir si l'annulation finale a été causée par un délai d'expiration.
Implémenter une API annulable
Une API qui doit prendre en charge l'annulation peut accepter un objet AbortSignal et utiliser son état pour déclencher la gestion de l'annulation lorsque nécessaire.
Une API basée sur Promise doit répondre au signal d'annulation en rejetant toute promesse non résolue avec la reason d'annulation d'AbortSignal.
Par exemple, considérez la fonction myCoolPromiseAPI ci-dessous, qui prend un signal et retourne une promesse.
La promesse est rejetée immédiatement si le signal est déjà annulé ou si l'évènement d'annulation est détecté.
Sinon, elle s'exécute normalement et se résout ensuite.
function myCoolPromiseAPI(/* …, */ { signal }) {
return new Promise((resolve, reject) => {
// Si le signal est déjà annulé, lancez immédiatement une exception pour rejeter la promesse.
signal.throwIfAborted();
// Effectue le but principal de l'API
// Appel resolve(result) une fois terminé.
// Observe les signaux 'abort'
// Le passage de `once: true` garantit que la promesse peut être collectée par le ramasse-miettes après l'appel à abort
signal.addEventListener(
"abort",
() => {
// Arrête l'opération principale
// Rejette la promesse avec la raison de l'annulation.
reject(signal.reason);
},
{ once: true },
);
});
}
L'API pourrait alors être utilisée comme suit.
Notez que AbortController.abort() est appelé pour annuler l'opération.
const controller = new AbortController();
const signal = controller.signal;
startSpinner();
myCoolPromiseAPI({ /* …, */ signal })
.then((result) => {})
.catch((err) => {
if (err.name === "AbortError") return;
showUserErrorMessage();
})
.then(() => stopSpinner());
controller.abort();
Les API qui ne retournent pas de promesses peuvent réagir de manière similaire. Dans certains cas, il peut être pertinent d'absorber le signal.
Spécifications
| Specification |
|---|
| DOM> # interface-AbortSignal> |
Compatibilité des navigateurs
Voir aussi
- L'API Fetch
- Récupération abortable par Jake Archibald