Introduzione
Quando si lavora con la Fetch API, non basta saper inviare una richiesta HTTP e leggere la risposta. In molte applicazioni reali è fondamentale anche interrompere una richiesta in corso. Pensiamo a questi casi:
- l’utente cambia pagina prima che il dato sia arrivato;
- stai cercando risultati mentre l’utente digita e vuoi annullare le richieste precedenti;
- un componente viene smontato e non vuoi aggiornare lo stato con una risposta ormai inutile;
- vuoi impostare un timeout manuale per evitare attese troppo lunghe.
Per gestire tutto questo, JavaScript mette a disposizione AbortController, un’API moderna e molto utile che si integra perfettamente con fetch(). In questo tutorial vedremo come usarla in modo pratico, con un esempio concreto e un approccio adatto a un progetto reale.
Codice completo
// Esempio pratico: ricerca utenti con annullamento delle richieste precedenti
// Simula una ricerca che parte mentre l´utente digita.
// Se arriva una nuova richiesta, quella precedente viene annullata.
const input = document.querySelector("#search");
const results = document.querySelector("#results");
let controller = null;
async function searchUsers(query) {
// Se esiste già una richiesta in corso, la annulliamo
if (controller) {
controller.abort();
}
// Creiamo un nuovo controller per la nuova richiesta
controller = new AbortController();
const { signal } = controller;
try {
results.innerHTML = "<p>Ricerca in corso...</p>";
const response = await fetch(
`https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(query)}`,
{
method: "GET",
signal
}
);
// Verifichiamo che la risposta HTTP sia valida
if (!response.ok) {
throw new Error(`Errore HTTP: ${response.status}`);
}
const users = await response.json();
if (users.length === 0) {
results.innerHTML = "<p>Nessun risultato trovato.</p>";
return;
}
results.innerHTML = `
<ul>
${users
.map(
(user) =>
`<li><strong>${user.name}</strong> - ${user.email}</li>`
)
.join("")}
</ul>
`;
} catch (error) {
// Se la richiesta è stata annullata, non la trattiamo come errore “vero”
if (error.name === "AbortError") {
console.log("Richiesta annullata:", query);
return;
}
results.innerHTML = `<p>Si è verificato un errore: ${error.message}</p>`;
}
}
// Debounce semplice: aspetta che l´utente smetta di digitare
let timeoutId = null;
input.addEventListener("input", (event) => {
const query = event.target.value.trim();
clearTimeout(timeoutId);
if (query.length < 2) {
results.innerHTML = "<p>Digita almeno 2 caratteri.</p>";
return;
}
timeoutId = setTimeout(() => {
searchUsers(query);
}, 400);
}); Spiegazione
Il cuore di questo esempio è AbortController. Si tratta di un oggetto che crea un segnale di abort, passato a fetch() tramite l’opzione signal. Quando chiamiamo controller.abort(), la richiesta HTTP viene interrotta.
1. Creazione del controller
Ogni volta che avviamo una nuova ricerca, creiamo un nuovo controller:
controller = new AbortController();
const { signal } = controller; Il signal è il “canale” che fetch ascolta per capire se deve fermarsi.
2. Collegamento con fetch()
La richiesta viene inviata così:
const response = await fetch(url, {
method: "GET",
signal
}); Se in seguito eseguiamo controller.abort(), la fetch viene annullata immediatamente.
3. Annullare la richiesta precedente
Prima di avviare una nuova ricerca, controlliamo se esiste già una richiesta in corso:
if (controller) {
controller.abort();
} Questo comportamento è molto utile nelle interfacce di ricerca in tempo reale. Senza annullamento, potresti ricevere risposte fuori ordine: ad esempio una richiesta più vecchia potrebbe arrivare dopo quella più recente e sovrascrivere i dati corretti.
4. Gestione dell’errore di abort
Quando una fetch viene annullata, JavaScript genera un errore con nome AbortError. Nel blocco catch lo intercettiamo e lo trattiamo separatamente:
if (error.name === "AbortError") {
console.log("Richiesta annullata:", query);
return;
} Questo è importante perché l’annullamento non è un errore applicativo: è un’azione voluta dal programma.
5. Debounce per ridurre le richieste
Nell’esempio abbiamo aggiunto anche un piccolo debounce. Significa che aspettiamo 400 ms dall’ultimo input prima di partire con la ricerca. Questo evita di inviare una richiesta a ogni singolo tasto premuto.
Il debounce e l’abort si completano bene:
- debounce riduce il numero di richieste;
- abort impedisce che richieste obsolete completino il loro ciclo.
Best practice
- Usa AbortController quando hai richieste “sostituibili”: ricerca live, autocomplete, filtri dinamici, caricamento di dati legati a una vista temporanea.
- Tratta AbortError separatamente: non mostrare messaggi di errore all’utente per richieste annullate intenzionalmente.
- Abbina abort e debounce: è una combinazione molto efficace per migliorare UX e prestazioni.
- Controlla sempre response.ok: l’annullamento non sostituisce la gestione degli errori HTTP reali, come 404 o 500.
- Evita di riutilizzare lo stesso controller dopo un abort: crea sempre un nuovo AbortController per ogni nuova richiesta.
- Pulisci le richieste quando un componente si smonta, soprattutto in framework come React, Vue o Svelte.
Timeout manuale con AbortController
Un altro utilizzo pratico è simulare un timeout. Fetch non ha un timeout nativo, ma possiamo costruirlo facilmente:
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timerId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
signal: controller.signal
});
if (!response.ok) {
throw new Error(`Errore HTTP: ${response.status}`);
}
return await response.json();
} finally {
clearTimeout(timerId);
}
} Questa funzione annulla la richiesta se non termina entro il tempo stabilito. È una soluzione semplice e molto utile in ambienti reali.
Riepilogo
AbortController è una delle funzionalità più pratiche da conoscere quando si usa la Fetch API. Ti permette di:
- annullare richieste obsolete;
- evitare aggiornamenti errati dell’interfaccia;
- migliorare la reattività dell’applicazione;
- implementare timeout personalizzati.
In sintesi, non pensare a fetch() solo come a un modo per scaricare dati: in un’app moderna devi anche gestire il ciclo di vita della richiesta. Saper interrompere una chiamata al momento giusto è una competenza concreta, molto apprezzata nello sviluppo frontend e full stack.
Approfondisci con risorse ufficiali
- MDN Web Docs - AbortController: documentazione completa sull’API e sui suoi metodi.
- MDN Web Docs - Fetch API: riferimento ufficiale per opzioni, response e gestione delle richieste.
- WHATWG Fetch Standard: specifica tecnica della Fetch API.
- MDN Web Docs - AbortSignal: approfondimento sul segnale usato per interrompere operazioni asincrone.
