AbortController
Permet d’interrompre une opération asynchrone, par exemple une requête fetch(), et de détacher plusieurs gestionnaires d’événements en un seul appel à abort().
- Renvoie
- Un nouvel objet
AbortControlleravec une propriétésignal.
Exemple
#const controller = new AbortController()
const { signal } = controller
signal.addEventListener('abort', () => console.log('Interrompu :', signal.reason.name))
const request = fetch('data:application/json,{"courses":12}', { signal })
controller.abort()
try {
await request
} catch (error) {
console.log('fetch rejeté :', error.name)
}
console.log(signal.aborted)L’événement abort apparaît immédiatement pendant controller.abort(). La promesse de la requête est rejetée avec une erreur AbortError, que le bloc catch intercepte.
Définition et utilisation
#AbortController se compose de deux parties : un contrôleur avec une méthode abort(), et un signal (controller.signal) que vous transmettez à une opération, par exemple fetch(url, { signal }). Appeler abort() indique à toutes les opérations utilisant ce signal qu’elles doivent s’arrêter. Une requête fetch() interrompue rejette sa promesse avec une erreur nommée AbortError.
Le signal a une propriété aborted (si l’opération a été interrompue), reason (la raison de l’interruption : par défaut l’erreur AbortError, mais vous pouvez transmettre la vôtre à abort()) et déclenche un événement abort. La méthode signal.throwIfAborted() lève la raison de l’interruption si le signal est déjà interrompu, ce qui est utile dans des fonctions plus longues. Un contrôleur ne sert qu’une seule fois : un signal interrompu le reste pour toujours, donc créez un nouveau contrôleur pour l’opération suivante.
Vous pouvez transmettre ce même signal à addEventListener(), dans l’option signal. Après abort(), tous ces gestionnaires sont détachés, sans avoir à mémoriser chaque fonction séparément. Des méthodes statiques créent aussi des signaux tout prêts : AbortSignal.timeout(ms) interrompt une opération au bout d’un certain temps (avec une erreur TimeoutError), et la plus récente AbortSignal.any([...]) combine plusieurs signaux en un seul.
Syntaxe
#const controller = new AbortController()
controller.signal
controller.abort()
controller.abort(reason)
signal.aborted
signal.reason
signal.throwIfAborted()
AbortSignal.timeout(ms)Paramètres
#reason
valeur quelconque, optionnel
La raison de l’interruption, transmise àabort(). Elle atterrit danssignal.reasonet dans l’erreur qui rejette la promesse. Par défaut, c’est une erreurDOMExceptionnomméeAbortError.
Plus d’exemples
#let controller
async function search(query) {
controller?.abort()
controller = new AbortController()
try {
const response = await fetch(`data:application/json,{"query":"${query}"}`, {
signal: controller.signal
})
const data = await response.json()
console.log('Résultats pour :', data.query)
} catch (error) {
if (error.name !== 'AbortError') throw error
console.log('Annulé :', query)
}
}
search('ja')
search('jav')
search('java')Chaque nouvelle recherche interrompt la requête précédente, donc on n’obtient un résultat que pour le dernier texte saisi. C’est ainsi que fonctionne un champ de recherche qui cherche déjà pendant la frappe.
const button = document.querySelector('#start')
const controller = new AbortController()
const { signal } = controller
button.addEventListener('click', () => console.log('Quiz démarré'), { signal })
button.addEventListener('click', () => console.log('Minuteur activé'), { signal })
document.addEventListener('keydown', () => console.log('Touche appuyée'), { signal })
button.click()
controller.abort()
button.click()
console.log('Gestionnaires détachés :', signal.aborted)Après controller.abort(), le second clic n’affiche plus rien. Les trois gestionnaires ont été détachés par un seul appel.
function wait(ms, signal) {
return new Promise((resolve, reject) => {
const timer = setTimeout(resolve, ms)
signal.addEventListener(
'abort',
() => {
clearTimeout(timer)
reject(signal.reason)
},
{ once: true }
)
})
}
const controller = new AbortController()
setTimeout(() => controller.abort(new Error('Délai de réponse dépassé')), 200)
try {
await wait(1000, controller.signal)
console.log('Réponse à temps')
} catch (error) {
console.log(error.message)
}La fonction wait() écoute l’événement abort sur le signal et rejette la promesse avec la raison transmise à abort(). Vous pouvez ajouter de la même façon une interruption à vos propres opérations asynchrones.
Compatibilité des navigateurs
#Largement disponible · depuis 2019
Fonctionne dans tous les navigateurs modernes, y compris sur mobile. Vous pouvez l’utiliser sans crainte.
- Chrome
- Edge
- Firefox
- Safari
Bonnes pratiques
#- Créez un nouvel
AbortControllerpour chaque nouvelle opération. Un signal interrompu le reste pour toujours et interromprait aussitôt chaque requête suivante. - Dans un bloc
catch, distinguez une interruption d’une vraie erreur avecerror.name === "AbortError". Une annulation par l’utilisateur n’a généralement pas besoin d’un message d’erreur. - Pour une simple limite de temps,
fetch(url, { signal: AbortSignal.timeout(5000) })suffit, sans créer de contrôleur.
Entrées associées
#- fetch()Envoie une requête HTTP et renvoie une promesse avec la réponse du serveur (un objet Response).
- EventTarget.removeEventListener()Détache une fonction de gestion d’événement ajoutée plus tôt avec addEventListener(). Exige la même fonction et le même type d’événement.
- EventTarget.addEventListener()Enregistre une fonction appelée à chaque fois qu’un événement se produit sur un élément, par ex. un clic.
- PromiseUn objet qui représente le résultat d’une opération disponible plus tard : une valeur en cas de succès, ou une erreur.
- try...catchCapture les erreurs levées dans un bloc try et les traite dans catch. Un finally optionnel s’exécute toujours.
- async functionDéclare une fonction asynchrone, qui renvoie toujours une promesse et peut utiliser le mot-clé await à l’intérieur.
Vous voyez une erreur ou il manque un exemple ? Écrivez-nous.