Зафиксируйте контракт: положительное attempts — общее число попыток, -1 — явно заданный бесконечный режим. Не превращайте исчерпание обычного счётчика в бесконечные повторы.
Обёртка fetch с автоматическим повтором при неудаче
Зафиксируйте контракт: положительное attempts — общее число попыток, -1 — явно заданный бесконечный режим. Не превращайте исчерпание обычного счётчика в бесконечные повторы.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо реализовать функцию‑обёртку над fetch, которая будет автоматически повторять запрос в случае ошибки. Функция принимает количество попыток и любые остальные аргументы, передаваемые в fetch. После каждой неудачной попытки следует выполнить запрос ещё раз, пока количество оставшихся попыток не станет -1 (бесконечный режим). Пример сигнатуры функции:
async function fetchWithRetry(attempts, ...args) {
return fetch(...args);
}
Функция должна возвращать полученный от fetch промис или бросать ошибку, если все попытки исчерпаны.
Ответ
Зафиксируйте контракт: положительное attempts — общее число попыток, -1 — явно заданный бесконечный режим. Не превращайте исчерпание обычного счётчика в бесконечные повторы.
async function fetchWithRetry(attempts, ...args) {
if (!Number.isInteger(attempts) || (attempts !== -1 && attempts < 1)) {
throw new RangeError('attempts: положительное целое или -1');
}
const signal = args[1]?.signal ?? args[0]?.signal;
let remaining = attempts;
for (;;) {
if (signal?.aborted) throw signal.reason ?? new Error('Aborted');
try {
const response = await fetch(...args);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response;
} catch (error) {
if (signal?.aborted || remaining === 1) throw error;
if (remaining !== -1) remaining--;
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
Для учебного условия повторяются все ошибки, включая HTTP. В production нужны ограничения по статусам, backoff, отмена и таймаут. POST с побочными эффектами нельзя повторять без идемпотентности; одноразовый stream-body необходимо создавать заново для каждой попытки.