N-попыток промиса вернуть resolve
const double = (x) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.3) {
resolve(x);
} else {
reject(x);
}
}, 1000);
});
const doubleWithRetry = withRetry(double, 3);
let ok = 0;
let err = 0;
const N = 10_000;
for (let i = 0; i < N; i++) {
doubleWithRetry(10)
.then((x) => ok++)
.catch((x) => err++);
}
setTimeout(() => {
console.log({ ok, err });
}, 1000);
Здравствуйте, есть такая функция – double – она возвращает выполненный или отклоненный промис, в зависимости от Math.random()
Мне нужно написать функцию-декоратор, которая будет пытаться N-количество раз вернуть resolve из этого промиса. На последней попытке, если очередной раз промис отклонился, то нужно окончательно отклонить промис.
Мое решение не подходит – ментор сказал, что он просто запускает в цикле 3 промиса, насколько я понял. Вот мой код:
function withRetry(fn, n) {
return function retry(arg) {
return new Promise((resolve, reject) => {
for (let i = 0; i < n; i++) {
fn(arg)
.then(resolve)
.catch(() => {
if (i === n - 1) {
reject();
}
});
}
});
};
}
Важно: async/await использовать нельзя. Помогите пожалуйста решить задачу.
Ответы (2 шт):
нужно написать функцию-декоратор, которая будет пытаться N-количество раз вернуть resolve из этого промиса. На последней попытке, если очередной раз промис отклонился, то нужно окончательно отклонить промис.
Предложу такой вариант реализации такого декоратора...
const double = (x) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.3) {
resolve(x);
} else {
reject(x);
}
}, 1000);
});
const doubleWithRetry = withRetry(double, 3);
doubleWithRetry('тест')
.then(res => console.log('Хорошо', res))
.catch(err => console.log('Плохо', err))
//
function withRetry(cb, cnt) {
return x => {
return new Promise((resolve, reject) => {
test()
function test() {
cb(x)
.then(resolve)
.catch(e => {
if (--cnt === 0) return reject(e)
console.log('Повтор...')
test()
})
}
})
}
}
Думал над альтернативой рекурсивному решению. Пришёл к такому варианту:
- Создаём цепочку promise-ов, где длина цепочки зависит от макс. количества попыток (retry-ев)
- В каждом таком promise-е проверяем:
- если был успех в предыдущих promise-ax, то возвращаем
Promise.resolve - иначе запускаем функцию (
double). Если результат - успех, то запоминаем это. Возвращаем (явно или неявно)Promise.resolve - иначе (если результат - неуспех), то смотрим какая эта попытка по счёту. Если не последняя - возвращаем
Promise.resolve - иначе (если попытка была последняя) возвращаем
Promise.reject
- если был успех в предыдущих promise-ax, то возвращаем
- Возвращаем в качестве результата последний promise из цепочки
P.S. Вероятность успеха равна 0.657 (0.3 + 0.7 * 0.3 + 0.7 * 0.7 * 0.3), а не 0.76
P.P.S. В исходном коде передаваемое в double значение игнорируется везде где только можно, поэтому из цепочки promise-ов его не стал возвращать
Код решения:
const withRetry = (fn, retriesMaxCount) => (fnArg) => {
let promise = Promise.resolve();
let currentTry = 0;
let gotSuccess = false;
for (let i = 0; i < retriesMaxCount; i++) {
promise = promise.then(() => {
if (gotSuccess) {
return Promise.resolve();
}
return fn(fnArg)
.then(() => {
gotSuccess = true;
})
.catch(() => {
currentTry++;
return currentTry < retriesMaxCount ? Promise.resolve() : Promise.reject();
});
});
}
return promise;
};
// ниже подправленный код из вопроса для тестирования решения
const double = (x) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.3) {
resolve(x);
} else {
reject(x);
}
}, 50);
});
const doubleWithRetry = withRetry(double, 3);
let ok = 0;
let err = 0;
const N = 100000;
const testPromises = [];
for (let i = 0; i < N; i++) {
testPromises[i] = doubleWithRetry(10)
.then((x) => ok++)
.catch((x) => err++);
}
Promise.all(testPromises).then(() => console.log({ ok, err }));