Polski

Dowiedz się, jak używać AbortController w JavaScript, aby skutecznie anulować operacje asynchroniczne, takie jak żądania pobierania, timery i inne, zapewniając czystszy i wydajniejszy kod.

JavaScript AbortController: Opanowanie Anulowania Operacji Asynchronicznych

We współczesnym tworzeniu stron internetowych operacje asynchroniczne są wszechobecne. Pobieranie danych z interfejsów API, ustawianie timerów i obsługa interakcji użytkownika często obejmują kod, który działa niezależnie i potencjalnie przez dłuższy czas. Istnieją jednak scenariusze, w których należy anulować te operacje przed ich zakończeniem. Właśnie tutaj interfejs AbortController w JavaScript przychodzi z pomocą. Zapewnia czysty i wydajny sposób sygnalizowania żądań anulowania do operacji DOM i innych zadań asynchronicznych.

Zrozumienie potrzeby anulowania

Zanim zagłębimy się w szczegóły techniczne, zrozumiejmy, dlaczego anulowanie operacji asynchronicznych jest ważne. Rozważmy te typowe scenariusze:

Wprowadzenie do AbortController i AbortSignal

Interfejs AbortController został zaprojektowany w celu rozwiązania problemu anulowania operacji asynchronicznych. Składa się z dwóch kluczowych komponentów:

Podstawowe użycie: Anulowanie żądań pobierania

Zacznijmy od prostego przykładu anulowania żądania fetch:


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

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

// To cancel the fetch request:
controller.abort();

Wyjaśnienie:

  1. Tworzymy instancję AbortController.
  2. Uzyskujemy powiązany AbortSignal z controller.
  3. Przekazujemy signal do opcji fetch.
  4. Jeśli musimy anulować żądanie, wywołujemy controller.abort().
  5. W bloku .catch() sprawdzamy, czy błąd to AbortError. Jeśli tak, wiemy, że żądanie zostało anulowane.

Obsługa AbortError

Po wywołaniu controller.abort(), żądanie fetch zostanie odrzucone z AbortError. Ważne jest, aby odpowiednio obsłużyć ten błąd w swoim kodzie. Niezastosowanie się do tego może prowadzić do nieobsłużonych odrzuczeń obietnic i nieoczekiwanego zachowania.

Oto bardziej solidny przykład z obsługą błędów:


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(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
      return null; // Or throw the error to be handled further up
    } else {
      console.error('Fetch error:', error);
      throw error; // Re-throw the error to be handled further up
    }
  }
}

fetchData();

// To cancel the fetch request:
controller.abort();

Najlepsze praktyki dotyczące obsługi AbortError:

Anulowanie timerów z AbortSignal

AbortSignal może być również używany do anulowania timerów utworzonych za pomocą setTimeout lub setInterval. Wymaga to nieco więcej pracy ręcznej, ponieważ wbudowane funkcje timera nie obsługują bezpośrednio AbortSignal. Musisz utworzyć funkcję niestandardową, która nasłuchuje sygnału przerwania i czyści timer, gdy zostanie uruchomiony.


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 Aborted'));
    });
  });

  return timeoutPromise;
}

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


cancellableTimeout(() => {
  console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));

// To cancel the timeout:
controller.abort();

Wyjaśnienie:

  1. Funkcja cancellableTimeout przyjmuje wywołanie zwrotne, opóźnienie i AbortSignal jako argumenty.
  2. Ustawia setTimeout i przechowuje identyfikator limitu czasu.
  3. Dodaje do AbortSignal detektor zdarzeń, który nasłuchuje zdarzenia abort.
  4. Gdy zdarzenie abort zostanie uruchomione, detektor zdarzeń czyści limit czasu i odrzuca obietnicę.

Anulowanie detektorów zdarzeń

Podobnie jak w przypadku timerów, możesz użyć AbortSignal do anulowania detektorów zdarzeń. Jest to szczególnie przydatne, gdy chcesz usunąć detektory zdarzeń powiązane z komponentem, który jest odmontowywany.


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

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

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

// To cancel the event listener:
controller.abort();

Wyjaśnienie:

  1. Przekazujemy signal jako opcję do metody addEventListener.
  2. Gdy controller.abort() zostanie wywołane, detektor zdarzeń zostanie automatycznie usunięty.

AbortController w komponentach React

W React możesz użyć AbortController do anulowania operacji asynchronicznych, gdy komponent się odmontowuje. Jest to niezbędne, aby zapobiec wyciekom pamięci i błędom spowodowanym aktualizacją odmontowanych komponentów. Oto przykład używający haka 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(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          console.error('Fetch error:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Cancel the fetch request when the component unmounts
    };
  }, []); // Empty dependency array ensures this effect runs only once on mount

  return (
    
{data ? (

Data: {JSON.stringify(data)}

) : (

Loading...

)}
); } export default MyComponent;

Wyjaśnienie:

  1. Tworzymy AbortController wewnątrz haka useEffect.
  2. Przekazujemy signal do żądania fetch.
  3. Zwracamy funkcję czyszczącą z haka useEffect. Ta funkcja zostanie wywołana, gdy komponent się odmontowuje.
  4. Wewnątrz funkcji czyszczącej wywołujemy controller.abort(), aby anulować żądanie pobierania.

Zaawansowane przypadki użycia

Łańcuchowanie AbortSignal

Czasami możesz chcieć połączyć wiele AbortSignal razem. Na przykład możesz mieć komponent nadrzędny, który musi anulować operacje w swoich komponentach podrzędnych. Można to osiągnąć, tworząc nowy AbortController i przekazując jego sygnał zarówno do komponentów nadrzędnych, jak i podrzędnych.

Używanie AbortController z bibliotekami innych firm

Jeśli używasz biblioteki innej firmy, która nie obsługuje bezpośrednio AbortSignal, może być konieczne dostosowanie kodu do współpracy z mechanizmem anulowania biblioteki. Może to obejmować zawijanie asynchronicznych funkcji biblioteki we własne funkcje, które obsługują AbortSignal.

Korzyści z używania AbortController

Zgodność przeglądarki

AbortController jest szeroko obsługiwany we współczesnych przeglądarkach, w tym Chrome, Firefox, Safari i Edge. Możesz sprawdzić tabelę zgodności w dokumentach MDN Web Docs, aby uzyskać najnowsze informacje.

Polyfills

W przypadku starszych przeglądarek, które natywnie nie obsługują AbortController, można użyć polyfill. Polyfill to fragment kodu, który zapewnia funkcjonalność nowszej funkcji w starszych przeglądarkach. Istnieje kilka dostępnych online polyfillów AbortController.

Podsumowanie

Interfejs AbortController jest potężnym narzędziem do zarządzania operacjami asynchronicznymi w JavaScript. Używając AbortController, możesz pisać czystszy, wydajniejszy i bardziej niezawodny kod, który elegancko obsługuje anulowanie. Niezależnie od tego, czy pobierasz dane z interfejsów API, ustawiasz timery, czy zarządzasz detektorami zdarzeń, AbortController może pomóc w poprawie ogólnej jakości aplikacji internetowych.

Dalsza lektura