Español

Aprende a usar AbortController de JavaScript para cancelar eficazmente operaciones asíncronas como peticiones fetch, temporizadores y más, asegurando un código más limpio y eficiente.

AbortController de JavaScript: Dominando la Cancelación de Operaciones Asíncronas

En el desarrollo web moderno, las operaciones asíncronas son omnipresentes. Obtener datos de las API, configurar temporizadores y gestionar las interacciones del usuario a menudo implican código que se ejecuta de forma independiente y, potencialmente, durante un período prolongado. Sin embargo, existen escenarios en los que es necesario cancelar estas operaciones antes de que se completen. Aquí es donde la interfaz AbortController de JavaScript viene al rescate. Proporciona una forma limpia y eficiente de señalar solicitudes de cancelación a las operaciones del DOM y otras tareas asíncronas.

Comprendiendo la Necesidad de Cancelación

Antes de profundizar en los detalles técnicos, comprendamos por qué cancelar las operaciones asíncronas es importante. Considere estos escenarios comunes:

Introducción a AbortController y AbortSignal

La interfaz AbortController está diseñada para resolver el problema de la cancelación de operaciones asíncronas. Consta de dos componentes clave:

Uso básico: Cancelación de peticiones Fetch

Comencemos con un ejemplo sencillo de cancelación de una petición fetch:


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

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

// Para cancelar la petición fetch:
controller.abort();

Explicación:

  1. Creamos una instancia de AbortController.
  2. Obtenemos el AbortSignal asociado del controller.
  3. Pasamos la signal a las opciones de fetch.
  4. Si necesitamos cancelar la petición, llamamos a controller.abort().
  5. En el bloque .catch(), comprobamos si el error es un AbortError. Si lo es, sabemos que la petición fue cancelada.

Gestionando AbortError

Cuando se llama a controller.abort(), la petición fetch será rechazada con un AbortError. Es crucial gestionar este error de forma adecuada en su código. No hacerlo puede llevar a rechazos de promesas no gestionados y un comportamiento inesperado.

Aquí tiene un ejemplo más robusto con gestión de errores:


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(`¡Error HTTP! Estado: ${response.status}`);
    }
    const data = await response.json();
    console.log('Datos:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch abortado');
      return null; // O lanzar el error para que se gestione más arriba
    } else {
      console.error('Error de Fetch:', error);
      throw error; // Volver a lanzar el error para que se gestione más arriba
    }
  }
}

fetchData();

// Para cancelar la petición fetch:
controller.abort();

Mejores prácticas para gestionar AbortError:

Cancelando temporizadores con AbortSignal

El AbortSignal también se puede usar para cancelar temporizadores creados con setTimeout o setInterval. Esto requiere un poco más de trabajo manual, ya que las funciones de temporizador integradas no admiten directamente AbortSignal. Necesita crear una función personalizada que escuche la señal de interrupción y borre el temporizador cuando se active.


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('Tiempo de espera abortado'));
    });
  });

  return timeoutPromise;
}

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


cancellableTimeout(() => {
  console.log('Tiempo de espera ejecutado');
}, 2000, signal)
.then(() => console.log("Tiempo de espera finalizado correctamente"))
.catch(err => console.log(err));

// Para cancelar el tiempo de espera:
controller.abort();

Explicación:

  1. La función cancellableTimeout toma una devolución de llamada, un retraso y un AbortSignal como argumentos.
  2. Configura un setTimeout y guarda la ID del tiempo de espera.
  3. Añade un event listener al AbortSignal que escucha el evento abort.
  4. Cuando se activa el evento abort, el event listener borra el tiempo de espera y rechaza la promesa.

Cancelando Event Listeners

De forma similar a los temporizadores, puede usar AbortSignal para cancelar los escuchas de eventos. Esto es particularmente útil cuando quiere eliminar los escuchas de eventos asociados a un componente que se está desmontando.


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

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

button.addEventListener('click', () => {
  console.log('¡Botón pulsado!');
}, { signal });

// Para cancelar el event listener:
controller.abort();

Explicación:

  1. Pasamos la signal como una opción al método addEventListener.
  2. Cuando se llama a controller.abort(), el event listener se eliminará automáticamente.

AbortController en componentes React

En React, puede usar AbortController para cancelar las operaciones asíncronas cuando un componente se desmonta. Esto es esencial para evitar fugas de memoria y errores causados por la actualización de componentes desmontados. Aquí tiene un ejemplo utilizando el 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(`¡Error HTTP! Estado: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch abortado');
        } else {
          console.error('Error de Fetch:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Cancela la petición fetch cuando el componente se desmonta
    };
  }, []); // El array de dependencia vacío asegura que este efecto se ejecute solo una vez al montar

  return (
    
{data ? (

Datos: {JSON.stringify(data)}

) : (

Cargando...

)}
); } export default MyComponent;

Explicación:

  1. Creamos un AbortController dentro del hook useEffect.
  2. Pasamos la signal a la petición fetch.
  3. Devolvemos una función de limpieza del hook useEffect. Esta función se llamará cuando el componente se desmonte.
  4. Dentro de la función de limpieza, llamamos a controller.abort() para cancelar la petición fetch.

Casos de uso avanzados

Encadenamiento de AbortSignals

A veces, es posible que desee encadenar varios AbortSignal. Por ejemplo, es posible que tenga un componente principal que necesite cancelar las operaciones en sus componentes secundarios. Puede lograr esto creando un nuevo AbortController y pasando su señal tanto a los componentes principales como a los secundarios.

Uso de AbortController con bibliotecas de terceros

Si utiliza una biblioteca de terceros que no admite directamente AbortSignal, es posible que deba adaptar su código para que funcione con el mecanismo de cancelación de la biblioteca. Esto puede implicar encapsular las funciones asíncronas de la biblioteca en sus propias funciones que gestionan el AbortSignal.

Beneficios de usar AbortController

Compatibilidad del navegador

AbortController es ampliamente compatible con los navegadores modernos, incluidos Chrome, Firefox, Safari y Edge. Puede consultar la tabla de compatibilidad en MDN Web Docs para obtener la información más reciente.

Polyfills

Para los navegadores más antiguos que no admiten de forma nativa AbortController, puede usar un polyfill. Un polyfill es un fragmento de código que proporciona la funcionalidad de una función más reciente en navegadores más antiguos. Hay varios polyfills de AbortController disponibles en línea.

Conclusión

La interfaz AbortController es una herramienta potente para gestionar las operaciones asíncronas en JavaScript. Al usar AbortController, puede escribir código más limpio, con mejor rendimiento y más robusto que gestiona la cancelación con elegancia. Ya sea que esté obteniendo datos de las API, configurando temporizadores o gestionando los escuchas de eventos, AbortController puede ayudarle a mejorar la calidad general de sus aplicaciones web.

Lecturas adicionales