È noto che fetch ritorna una prime. JavaScript comunemente non ha però il concetto di “interruzione” di una promise. Come possiamo fare quindi ad interrompere una chiamata fetch?
C’è uno speciale operatore built-in per questo scopo: AbortController, il quale può essere usato per interrompere fetch ed anche altri tasks asincroni.
L’utilizzo è molto semplice:
- Step 1: Creare un controller:
Create a controller:
let controller = new AbortController();
Un controller è un oggetto estremamente semplice.
- Possiede un solo metodo
abort(), - Ed un’unica proprietà
signalche permette di impostare dei controllori di evento (event listeneres).
Quando viene invocato il metodo abort():
controller.signalemette l’evento"abort".- la proprietà
controller.signal.aborteddiventatrue.
Generalmente il processo si suddivide in due parti:
- quella che esegue l’operazione annullabile, imposta il listener su
controller.signal. - quella che annulla: essa chiama
controller.abort()quando necessario.
Qui vediamo un esempio completo (ancora senza fetch):
let controller = new AbortController();
let signal = controller.signal;
// La parte che esegue un'operazione annullabile
// ottiene l'oggetto "signal"
// ed imposta il controllore di eventi di attivarsi quando viene invocato controller.abort()
signal.addEventListener('abort', () => alert("abort!"));
// L'altra parte, che cancella (in un qualsiasi punto più avanti):
controller.abort(); // abort!
// L'evento si innesca e il segnale di aborted diventa true
alert(signal.aborted); // true
Potremmo implementare lo stesso tipo controllo di eventi nel nostro codice, anche senza l’oggetto AbortController.
Ma ciò che dà valore è che fetch è ottimizzato per lavorare con l’oggetto AbortController, poiché è integrato.
let controller = new AbortController();
fetch(url, {
signal: controller.signal
});
Il metodo fetch è in grado di lavorare con AbortController. Starà in attesa dell’event abort su signal.
Ora, per abortire la richiesta, invochiamo controller.abort():
controller.abort();
Abbiamo finito: fetch riceve l’event da signal ed interrompe la richiesta.
Quando un fetch viene interrotto, la promise viene rifiutata con un errore AbortError, quindi dovremmo prevedere una sua gestione, e.g. all’interno di un try..catch.
Qui vediamo l’esempio completo con fetch, che viene interrotto dopo 1 secondo:
// interrompi in 1 secondo
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') { // gestisci l'abort()
alert("Aborted!");
} else {
throw err;
}
}
AbortController è scalabile
AbortController è scalabile e può cancellare fetches multipli in una volta.
Ad esempio, qui valutiamo molti url in parallelo e il controller li interrompe tutti:
let urls = [...]; // un elenco di urls di cui fare il fetch in parallelo
let controller = new AbortController();
// un array di fetch promises
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));
let results = await Promise.all(fetchJobs);
// se controller.abort() è chiamato da qualche parte del codice
// saranno interrotte tutte le fetches
Se abbiamo dei jobs asincroni, oltre a fetch, possiamo usare un singoloAbortController per fermarli insieme alle fetches.
Dobbiamo solo ascoltare il suo evento abort:
let urls = [...];
let controller = new AbortController();
let ourJob = new Promise((resolve, reject) => { // un nostro task
...
controller.signal.addEventListener('abort', reject);
});
let fetchJobs = urls.map(url => fetch(url, { // fetches
signal: controller.signal
}));
// Attende per le and ed il nostro task in parallelo
let results = await Promise.all([...fetchJobs, ourJob]);
// se controller.abort() è chiamato da qualche parte del codice
// saranno interrotte tutte le fetches e ourJob
Riepilogo
AbortControllerè un semplice oggetto che genera eventi diabortsulla proprietàsignalquando viene invocato il metodoabort()(ed imposta anchesignal.abortedatrue).fetchsi integra con esso: se passiamo la proprietàsignalcome opzione,fetchè in grado di controllarla, quindi sarà possibile interrompere l’operazione difetch.- Possiamo utilizzare
AbortControllernel nostro codice. La chiamataabort()" → “ascolterà l’eventoabort”. Possiamo utilizzarla anche senzafetch.
Commenti
<code>, per molte righe – includile nel tag<pre>, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)