Разбор #Задачка_29
Первый вариант
Решение через рекурсию от Артема
const retry = async (fn, { retries }) => {
try {
return await fn();
} catch (e) {
if (retries > 0) {
return retry(fn, { retries: retries - 1 });
}
throw e;
}
};
Лаконичное решение, мне очень нравится. Но оно не совсем подходит для прода. Автор описал свои мысли почему:
> Минусы решения в коде
1. Надо добавить параметр timeoutMs в config
2. Обрабатывать ошибку, которую может вернуть fn, возможно, не надо ретраить
3. Вероятно добавить retryWhen колбэк параметром
4. Нужны стратегии
(линейная, инкрементальная и тд), через какое время делать ретрай
5. Нужны логи и стратегии их отображения: когда и куда писать, например для дебага
6. Описание, типы, тесты и валидация аргументов, конфига
(все ли поля используем, или есть опечатки)
> Минусы решения в подходе
1. Переданная функция должна быть идемпотентной
(если повезет - это at least once, явно не exact once)
2. В случаи АПИ запроса - ретраить нужно не все статусы ответов, чтобы не заДДОсить
Пункты про стратегию повторных ретраев очень важны. Ведь без таких стратегий можно себя задедосить. Такое, например,
было в Signal.
Стратегию повторных ретраев с задержкой по времени принято называть экспоненциальной выдержкой или Exponential Backoff. Больше про нее можно прочитать
здесь
Второй вариант
Из задачи #Задачка_28 мы знаем, что рекурсию можно заменить циклом. #Задачка_29 не исключение
Решение через цикл от aslanator:
async function retry(callback, options = { retries: 0 }) {
let { retries } = options;
while (retries > 0) {
try {
return await callback();
} catch (e) {
retries--;
}
}
return await callback();
}
> Ход мысли тут достаточно прямолинейный, нам просто нужно отлавливать ошибку и заново запускать скрипт, пока не истекут попытки. Чтобы в конце вывести ошибку, я делаю повтором меньше на 1, а в конце просто отдаю callback.
Вроде больше нечего сказать, несколько раз делал подобное в реальных проектах на работе.
Третий вариант
Хитрое и очень прикольное решение от Владимира
const retry = (func, { retries = 3 }) => {
const promises = Array.from({length: retries}, () => func());
return Promise.any(promises);
}
Размышление автора про минусы в своем подходе:
> Проблема кроется в самом Promise.any. Из документации следует, что данный метод вызывает все промисы из массива и дожидается, когда один из них будет выполнен или выкинет ошибку в случае не исполнения никакого промиса.
В конечном итоге, если данная функция будет вызвана с большим кол-во retries
(200 к примеру), то есть вероятность упасть в ошибку самого браузера по количеству запросов.