Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як реалізувати тайм-аут і повтор із затримкою для fetch?

fetch за замовчуванням не має тайм-ауту: запит до сервера, що завис, може «висіти» хвилинами. А тимчасові збої мережі чи відповідь 503 часто зникають після повтору.

Тайм-аут через AbortSignal.timeout():

try {
  const response = await fetch('/api/report', { signal: AbortSignal.timeout(5000) });
} catch (error) {
  if (error.name === 'TimeoutError') {
    showError('Сервер не відповів за 5 секунд');
  }
}

При тайм-ауті Promise відхиляється з TimeoutError, а при ручному скасуванні через AbortController - з AbortError. Це дає змогу розрізнити причини.

Поєднати тайм-аут і ручне скасування (користувач пішов зі сторінки) - AbortSignal.any():

const controller = new AbortController();
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(5000)]);

Повтор з експоненційною затримкою і випадковим розкидом:

async function fetchWithRetry(url, options = {}, { retries = 3, baseDelay = 300 } = {}) {
  for (let attempt = 0; ; attempt++) {
    try {
      const response = await fetch(url, { ...options, signal: AbortSignal.timeout(5000) });
      if (response.status < 500 && response.status !== 429) {
        return response;                 // успіх або помилка клієнта - не повторюємо
      }
      if (attempt >= retries) return response;
    } catch (error) {
      if (attempt >= retries) throw error;
    }
    const delay = baseDelay * 2 ** attempt * (0.5 + Math.random());
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
}

Що важливо:

  • повторювати лише те, що має сенс повторювати: мережеві помилки, тайм-аути, 502/503/504, 429. Помилки 4xx (валідація, 403, 404) повтор не виправить;
  • ідемпотентність: GET повторювати безпечно. POST на створення замовлення - ні: перша спроба могла дійти до сервера, а загубилася лише відповідь. Для таких запитів потрібен ключ ідемпотентності, який сервер перевіряє;
  • розкид затримки (jitter) потрібен, щоб тисячі клієнтів після збою не повторювали запити синхронно й не «добили» сервер, що відновлюється;
  • Retry-After у відповіді 429/503 варто поважати замість власної затримки;
  • загальний бюджет часу важливіший за кількість спроб: користувач не чекатиме хвилину на три повтори з тайм-аутом по 20 секунд.

Докладніше в документації: AbortSignal.timeout()

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання