Этот вопрос проверяет умение реализовывать механизм повторных попыток (retry) для обработки временных сбоев в асинхронных операциях, что критически важно для повышения отказоустойчивости приложений.
Механизм повторных попыток (retry) — это паттерн проектирования, который повышает устойчивость приложения к временным сбоям. Вместо того чтобы немедленно сообщать об ошибке, система предпринимает несколько попыток выполнить операцию, прежде чем признать её неудачной. Это особенно полезно для операций, зависящих от внешних ресурсов: сетевых запросов, подключений к базе данных или вызовов сторонних API, где сбои часто носят временный характер.
Основная идея — обернуть вызов функции в цикл, который будет выполняться до тех пор, пока операция не завершится успешно или не будет исчерпан лимит попыток. Внутри цикла используется блок try/catch для перехвата исключений. При возникновении ошибки выполнение прерывается, увеличивается счётчик попыток, и после паузы цикл повторяется.
Простая повторная попытка без задержки может усугубить проблему, например, перегрузить упавший сервер. Поэтому применяют стратегии задержки:
Ниже приведён пример функции-обёртки с экспоненциальной задержкой и ограничением попыток.
async function retryWithBackoff(operation, maxRetries = 3, baseDelay = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// Пытаемся выполнить переданную асинхронную операцию
return await operation();
} catch (error) {
lastError = error;
console.warn(`Attempt ${attempt} failed:`, error.message);
// Если это последняя попытка — выходим из цикла
if (attempt === maxRetries) break;
// Вычисляем задержку: baseDelay * 2^(attempt-1)
const delay = baseDelay * Math.pow(2, attempt - 1);
// Добавляем небольшую случайность (jitter)
const jitter = delay * 0.1 * Math.random();
await new Promise(resolve => setTimeout(resolve, delay + jitter));
}
}
// Если все попытки исчерпаны, пробрасываем последнюю ошибку
throw lastError;
}
// Пример использования: функция, имитирующая ненадёжный сетевой запрос
async function fetchData() {
const shouldFail = Math.random() > 0.5;
if (shouldFail) throw new Error('Network timeout');
return { data: 'Success!' };
}
// Вызов с повторными попытками
retryWithBackoff(fetchData, 3, 1000)
.then(result => console.log('Result:', result))
.catch(error => console.error('All attempts failed:', error));Retry-логика применяется в самых разных сценариях:
Важно не применять retry для ошибок, которые не являются временными (например, 404 Not Found или ошибки валидации 4xx). Также следует настраивать разумные лимиты попыток и задержек, чтобы не блокировать систему надолго.
Вывод: Механизм повторных попыток — это ключевой инструмент для создания отказоустойчивых приложений. Его стоит применять для операций, где сбои вероятны, но носят временный характер, таких как сетевые запросы или взаимодействие с внешними сервисами. Использование стратегии exponential backoff с jitter делает реализацию эффективной и безопасной для распределённых систем.