Български

Научете как да използвате AbortController в JavaScript за ефективно прекъсване на асинхронни операции като fetch заявки, таймери и други, осигурявайки по-чист и производителен код.

JavaScript AbortController: Овладяване на прекъсването на асинхронни операции

В съвременната уеб разработка асинхронните операции са повсеместни. Извличането на данни от API, задаването на таймери и обработката на потребителски взаимодействия често включват код, който се изпълнява независимо и потенциално за продължителен период от време. Има обаче сценарии, в които трябва да прекъснете тези операции, преди те да приключат. Тук на помощ идва интерфейсът AbortController в JavaScript. Той предоставя чист и ефективен начин за подаване на сигнали за заявки за прекъсване към DOM операции и други асинхронни задачи.

Разбиране на нуждата от прекъсване

Преди да се потопим в техническите детайли, нека разберем защо прекъсването на асинхронни операции е важно. Разгледайте тези често срещани сценарии:

Представяне на AbortController и AbortSignal

Интерфейсът AbortController е създаден да реши проблема с прекъсването на асинхронни операции. Той се състои от два ключови компонента:

Основна употреба: Прекъсване на Fetch заявки

Нека започнем с прост пример за прекъсване на 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();

Обяснение:

  1. Създаваме инстанция на AbortController.
  2. Получаваме свързания AbortSignal от controller.
  3. Предаваме signal към опциите на fetch.
  4. Ако трябва да прекъснем заявката, извикваме controller.abort().
  5. В блока .catch() проверяваме дали грешката е AbortError. Ако е така, знаем, че заявката е била прекъсната.

Обработка на AbortError

Когато controller.abort() бъде извикан, fetch заявката ще бъде отхвърлена с AbortError. От решаващо значение е да обработите тази грешка по подходящ начин във вашия код. Ако не го направите, това може да доведе до необработени отхвърляния на promise и неочаквано поведение.

Ето един по-стабилен пример с обработка на грешки:


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

asynс 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();

Най-добри практики за обработка на AbortError:

Прекъсване на таймери с AbortSignal

AbortSignal може да се използва и за прекъсване на таймери, създадени със setTimeout или setInterval. Това изисква малко повече ръчна работа, тъй като вградените функции за таймери не поддържат директно AbortSignal. Трябва да създадете персонализирана функция, която слуша за сигнала за прекъсване и изчиства таймера, когато той бъде задействан.


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();

Обяснение:

  1. Функцията cancellableTimeout приема като аргументи callback функция, забавяне и AbortSignal.
  2. Тя настройва setTimeout и съхранява ID-то на таймера.
  3. Тя добавя event listener към AbortSignal, който слуша за събитието abort.
  4. Когато събитието abort се задейства, event listener-ът изчиства таймера и отхвърля promise-а.

Прекъсване на Event Listeners

Подобно на таймерите, можете да използвате AbortSignal за прекъсване на event listeners. Това е особено полезно, когато искате да премахнете event listeners, свързани с компонент, който се демонтира.


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();

Обяснение:

  1. Предаваме signal като опция на метода addEventListener.
  2. Когато controller.abort() бъде извикан, event listener-ът ще бъде автоматично премахнат.

AbortController в React компоненти

В React можете да използвате AbortController, за да прекъсвате асинхронни операции, когато даден компонент се демонтира. Това е от съществено значение за предотвратяване на изтичане на памет и грешки, причинени от актуализиране на демонтирани компоненти. Ето един пример, използващ useEffect hook:


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;

Обяснение:

  1. Създаваме AbortController в рамките на useEffect hook.
  2. Предаваме signal на fetch заявката.
  3. Връщаме почистваща функция от useEffect hook. Тази функция ще бъде извикана, когато компонентът се демонтира.
  4. Вътре в почистващата функция извикваме controller.abort(), за да прекъснем fetch заявката.

Разширени случаи на употреба

Свързване на AbortSignal-и

Понякога може да искате да свържете няколко AbortSignal-а заедно. Например, може да имате родителски компонент, който трябва да прекъсва операции в своите дъщерни компоненти. Можете да постигнете това, като създадете нов AbortController и предадете неговия сигнал както на родителския, така и на дъщерните компоненти.

Използване на AbortController с библиотеки на трети страни

Ако използвате библиотека на трета страна, която не поддържа директно AbortSignal, може да се наложи да адаптирате кода си, за да работи с механизма за прекъсване на библиотеката. Това може да включва обвиване на асинхронните функции на библиотеката във ваши собствени функции, които обработват AbortSignal.

Предимства от използването на AbortController

Съвместимост с браузъри

AbortController се поддържа широко в съвременните браузъри, включително Chrome, Firefox, Safari и Edge. Можете да проверите таблицата за съвместимост в MDN Web Docs за най-новата информация.

Полифили

За по-стари браузъри, които не поддържат нативно AbortController, можете да използвате полифил (polyfill). Полифилът е част от код, който предоставя функционалността на по-нова функция в по-стари браузъри. В интернет има няколко налични полифила за AbortController.

Заключение

Интерфейсът AbortController е мощен инструмент за управление на асинхронни операции в JavaScript. Използвайки AbortController, можете да пишете по-чист, по-производителен и по-стабилен код, който обработва прекъсването елегантно. Независимо дали извличате данни от API, задавате таймери или управлявате event listeners, AbortController може да ви помогне да подобрите цялостното качество на вашите уеб приложения.

Допълнителни материали за четене

JavaScript AbortController: Овладяване на прекъсването на асинхронни операции | MLOG