Italiano

Scopri come utilizzare AbortController di JavaScript per annullare efficacemente operazioni asincrone come richieste fetch, timer e altro, garantendo un codice più pulito ed efficiente.

JavaScript AbortController: Gestire la cancellazione di operazioni asincrone

Nello sviluppo web moderno, le operazioni asincrone sono onnipresenti. Recuperare dati dalle API, impostare timer e gestire le interazioni dell'utente spesso comportano codice che viene eseguito in modo indipendente e potenzialmente per una durata estesa. Tuttavia, ci sono scenari in cui è necessario annullare queste operazioni prima che vengano completate. È qui che l'interfaccia AbortController in JavaScript viene in soccorso. Fornisce un modo pulito ed efficiente per segnalare richieste di annullamento alle operazioni DOM e ad altre attività asincrone.

Comprendere la necessità di cancellazione

Prima di approfondire i dettagli tecnici, cerchiamo di capire perché l'annullamento delle operazioni asincrone è importante. Considera questi scenari comuni:

Introduzione a AbortController e AbortSignal

L'interfaccia AbortController è progettata per risolvere il problema dell'annullamento delle operazioni asincrone. Si compone di due componenti chiave:

Utilizzo di base: annullamento delle richieste Fetch

Cominciamo con un semplice esempio di annullamento di una richiesta fetch:


const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/data', { signal })
  .then(response => {
    if (!response.ok) {
      throw new Error(`Errore HTTP! Stato: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Dati:', data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch interrotta');
    } else {
      console.error('Errore Fetch:', error);
    }
  });

// Per annullare la richiesta fetch:
controller.abort();

Spiegazione:

  1. Creiamo un'istanza di AbortController.
  2. Otteniamo l'AbortSignal associato dal controller.
  3. Passiamo il signal alle opzioni fetch.
  4. Se dobbiamo annullare la richiesta, chiamiamo controller.abort().
  5. Nel blocco .catch(), verifichiamo se l'errore è un AbortError. In tal caso, sappiamo che la richiesta è stata annullata.

Gestione di AbortError

Quando viene chiamato controller.abort(), la richiesta fetch verrà rifiutata con un AbortError. È fondamentale gestire questo errore in modo appropriato nel codice. In caso contrario, possono verificarsi reiezioni di promise non gestite e comportamenti imprevisti.

Ecco un esempio più robusto con la gestione degli errori:


const controller = new AbortController();
const signal = controller.signal;

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data', { signal });
    if (!response.ok) {
      throw new Error(`Errore HTTP! Stato: ${response.status}`);
    }
    const data = await response.json();
    console.log('Dati:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch interrotta');
      return null; // Oppure lancia l'errore per essere gestito più in alto
    } else {
      console.error('Errore Fetch:', error);
      throw error; // Rilancia l'errore per essere gestito più in alto
    }
  }
}

fetchData();

// Per annullare la richiesta fetch:
controller.abort();

Best practice per la gestione di AbortError:

Annullamento dei timer con AbortSignal

L'AbortSignal può essere utilizzato anche per annullare i timer creati con setTimeout o setInterval. Ciò richiede un po' più di lavoro manuale, poiché le funzioni timer integrate non supportano direttamente AbortSignal. È necessario creare una funzione personalizzata che ascolti il segnale di interruzione e cancelli il timer quando viene attivato.


function cancellableTimeout(callback, delay, signal) {
  let timeoutId;

  const timeoutPromise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(() => {
      resolve(callback());
    }, delay);

    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new Error('Timeout interrotto'));
    });
  });

  return timeoutPromise;
}

const controller = new AbortController();
const signal = controller.signal;


cancellableTimeout(() => {
  console.log('Timeout eseguito');
}, 2000, signal)
.then(() => console.log("Timeout terminato con successo"))
.catch(err => console.log(err));

// Per annullare il timeout:
controller.abort();

Spiegazione:

  1. La funzione cancellableTimeout accetta un callback, un ritardo e un AbortSignal come argomenti.
  2. Imposta un setTimeout e memorizza l'ID del timeout.
  3. Aggiunge un listener di eventi all'AbortSignal che ascolta l'evento abort.
  4. Quando l'evento abort viene attivato, il listener di eventi cancella il timeout e rifiuta la promise.

Annullamento dei listener di eventi

Similmente ai timer, puoi usare AbortSignal per annullare i listener di eventi. Questo è particolarmente utile quando si desidera rimuovere i listener di eventi associati a un componente che viene smontato.


const controller = new AbortController();
const signal = controller.signal;

const button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log('Pulsante cliccato!');
}, { signal });

// Per annullare il listener di eventi:
controller.abort();

Spiegazione:

  1. Passiamo il signal come opzione al metodo addEventListener.
  2. Quando viene chiamato controller.abort(), il listener di eventi verrà rimosso automaticamente.

AbortController nei componenti React

In React, puoi usare AbortController per annullare le operazioni asincrone quando un componente viene smontato. Questo è essenziale per prevenire perdite di memoria ed errori causati dall'aggiornamento di componenti smontati. Ecco un esempio usando l'hook useEffect:


import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data', { signal });
        if (!response.ok) {
          throw new Error(`Errore HTTP! Stato: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch interrotta');
        } else {
          console.error('Errore Fetch:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Annulla la richiesta fetch quando il componente viene smontato
    };
  }, []); // L'array di dipendenze vuoto assicura che questo effetto venga eseguito una sola volta al montaggio

  return (
    
{data ? (

Dati: {JSON.stringify(data)}

) : (

Caricamento...

)}
); } export default MyComponent;

Spiegazione:

  1. Creiamo un AbortController all'interno dell'hook useEffect.
  2. Passiamo il signal alla richiesta fetch.
  3. Restituiamo una funzione di pulizia dall'hook useEffect. Questa funzione verrà chiamata quando il componente viene smontato.
  4. All'interno della funzione di pulizia, chiamiamo controller.abort() per annullare la richiesta fetch.

Casi d'uso avanzati

Concatenamento di AbortSignal

A volte, potresti voler concatenare più AbortSignal insieme. Ad esempio, potresti avere un componente padre che deve annullare le operazioni nei suoi componenti figlio. Puoi ottenere questo risultato creando un nuovo AbortController e passando il suo segnale sia ai componenti padre che figlio.

Utilizzo di AbortController con librerie di terze parti

Se stai usando una libreria di terze parti che non supporta direttamente AbortSignal, potresti dover adattare il tuo codice per funzionare con il meccanismo di annullamento della libreria. Ciò potrebbe comportare l'incapsulamento delle funzioni asincrone della libreria nelle tue funzioni che gestiscono l'AbortSignal.

Vantaggi dell'utilizzo di AbortController

Compatibilità del browser

AbortController è ampiamente supportato nei browser moderni, inclusi Chrome, Firefox, Safari ed Edge. Puoi controllare la tabella di compatibilità su MDN Web Docs per le informazioni più recenti.

Polyfill

Per i browser meno recenti che non supportano nativamente AbortController, puoi usare un polyfill. Un polyfill è un frammento di codice che fornisce la funzionalità di una funzionalità più recente nei browser meno recenti. Sono disponibili diversi polyfill AbortController online.

Conclusione

L'interfaccia AbortController è uno strumento potente per la gestione delle operazioni asincrone in JavaScript. Utilizzando AbortController, puoi scrivere codice più pulito, più performante e più robusto che gestisce l'annullamento con garbo. Che tu stia recuperando dati dalle API, impostando timer o gestendo listener di eventi, AbortController può aiutarti a migliorare la qualità complessiva delle tue applicazioni web.

Ulteriori letture