N-попыток промиса вернуть resolve

Рейтинг: 1Ответов: 2Опубликовано: 18.04.2023
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 использовать нельзя. Помогите пожалуйста решить задачу.

Ответы

▲ 1

Думал над альтернативой рекурсивному решению. Пришёл к такому варианту:

  1. Создаём цепочку promise-ов, где длина цепочки зависит от макс. количества попыток (retry-ев)
  2. В каждом таком promise-е проверяем:
    • если был успех в предыдущих promise-ax, то возвращаем Promise.resolve
    • иначе запускаем функцию (double). Если результат - успех, то запоминаем это. Возвращаем (явно или неявно) Promise.resolve
    • иначе (если результат - неуспех), то смотрим какая эта попытка по счёту. Если не последняя - возвращаем Promise.resolve
    • иначе (если попытка была последняя) возвращаем Promise.reject
  3. Возвращаем в качестве результата последний 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 }));

▲ 0

нужно написать функцию-декоратор, которая будет пытаться 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()
          })
      }
    })
  }
}