Отмена запросов

Bliss.js предоставляет удобные методы работы с асинхронными операциями, включая сетевые запросы через fetch и собственные утилиты для управления потоками данных. Одной из ключевых возможностей является отмена запросов, что особенно важно при работе с динамическими интерфейсами, автоподгрузкой данных и обработкой пользовательских событий, где устаревшие запросы могут привести к неконсистентному состоянию приложения.


Использование AbortController

Для отмены запросов Bliss.js интегрируется с нативным API AbortController. Создание контроллера и привязка его к запросу позволяет остановить выполнение запроса в любой момент.

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

bliss.fetch('/api/data', { signal })
  .then(response => response.json())
  .then(data => console.log('Данные:', data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Запрос был отменен');
    } else {
      console.error('Ошибка запроса:', err);
    }
  });

// Отмена запроса через 2 секунды
setTimeout(() => controller.abort(), 2000);

Ключевые моменты:

  • AbortController создается один раз на каждый запрос.
  • Свойство signal передается в опции запроса.
  • Исключение AbortError позволяет корректно обработать отмену.

Множественные запросы и управление состоянием

Bliss.js позволяет запускать несколько параллельных запросов и управлять их состояниями с помощью внутренних утилит Bliss.Queue. Это особенно полезно, когда требуется отменять устаревшие запросы при быстром изменении данных, например, при поиске с автодополнением.

const queue = new Bliss.Queue({ concurrency: 1 });

function search(query) {
  const controller = new AbortController();
  queue.add(() => 
    bliss.fetch(`/api/search?q=${query}`, { signal: controller.signal })
      .then(res => res.json())
      .catch(err => {
        if (err.name === 'AbortError') return null;
        throw err;
      })
  );
  return controller;
}

let currentController = search('apple');

// Пользователь быстро вводит новый запрос
currentController.abort();
currentController = search('banana');

Выделенные принципы:

  • Bliss.Queue обеспечивает контроль за очередью запросов.
  • Каждому запросу соответствует свой AbortController.
  • При запуске нового запроса предыдущий можно безопасно отменять.

Отмена запросов с использованием таймаута

Bliss.js позволяет комбинировать AbortController с таймаутами, что позволяет автоматически отменять зависшие запросы.

function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const signal = controller.signal;

  const timer = setTimeout(() => controller.abort(), timeout);

  return bliss.fetch(url, { signal })
    .finally(() => clearTimeout(timer));
}

fetchWithTimeout('/api/data', 3000)
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') console.log('Запрос отменен по таймауту');
  });

Важные моменты:

  • Таймер гарантирует отмену запроса при превышении лимита.
  • finally используется для очистки таймера, предотвращая утечки памяти.

Интеграция с пользовательскими событиями

Отмена запросов особенно актуальна при интерактивных интерфейсах. Например, при переключении вкладок или скролле можно отменять все текущие запросы, чтобы не загружать устаревшие данные.

const controllers = new Set();

function loadData(endpoint) {
  const controller = new AbortController();
  controllers.add(controller);

  bliss.fetch(endpoint, { signal: controller.signal })
    .then(res => res.json())
    .then(data => console.log(data))
    .finally(() => controllers.delete(controller));

  return controller;
}

// Отмена всех текущих запросов при смене вкладки
function cancelAllRequests() {
  controllers.forEach(c => c.abort());
  controllers.clear();
}

Принципы работы:

  • Коллекция controllers хранит все активные запросы.
  • Метод abort() мгновенно завершает все запросы.
  • finally удаляет контроллер из коллекции, сохраняя актуальное состояние.

Совместная работа с async/await

Bliss.js поддерживает современный синтаксис async/await, что упрощает обработку отменяемых запросов и делает код читаемым и последовательным.

async function fetchData(endpoint) {
  const controller = new AbortController();
  try {
    const response = await bliss.fetch(endpoint, { signal: controller.signal });
    const data = await response.json();
    console.log(data);
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('Запрос отменен пользователем');
    } else {
      console.error(err);
    }
  }
  return controller;
}

const controller = fetchData('/api/items');
// Отмена через 1 секунду
setTimeout(() => controller.abort(), 1000);

Преимущества подхода:

  • Код становится линейным и легким для чтения.
  • Обработка ошибок и отмены запросов централизована.
  • Можно комбинировать с Promise.all и другими утилитами для параллельных запросов.

Практические рекомендации

  • Всегда использовать AbortController для запросов, которые могут устаревать.
  • Для списков или поиска с автоподгрузкой — отменять предыдущие запросы перед запуском новых.
  • Использовать таймауты для защиты от зависших запросов.
  • Хранить активные контроллеры в коллекции для массовой отмены.
  • Обрабатывать исключение AbortError отдельно, чтобы избежать логирования ошибок как критичных.

Bliss.js сочетает нативные возможности AbortController с собственными утилитами для очередей и управления асинхронностью, что позволяет строить производительные и отзывчивые интерфейсы, контролируя все активные сетевые операции.