Promise.allSettled() - 多个异步调用的重试策略

Mao*_*ion 1 javascript node.js promise typescript es6-promise

TL;DR:我正在寻找一种策略或示例来处理未知数量的承诺拒绝,并X在用于多个异步调用时重试它们一段时间Promise.allSettled()

读完这篇文章后:https://www.coreycleary.me/better-handling-of-rejections-using-promise-allsettled 我对这句话感到非常兴奋:

这很棒,因为我们仍然可以加载用户帐户信息,并稍后重试获取用户活动。(重试超出了本文的范围,并且有多种策略)

然而,在网上研究时,我发现完全没有具体的例子,甚至没有直接处理这个问题的帖子。

这是一些示例代码:

  public async test() {
    try {
      console.log('trying..');
      const multiAsync = [this.somethingA(), this.somethingB(), this.somethingC()];
      const [a, b, c] = await Promise.allSettled(multiAsync);
    } catch (e) {
      console.log('catch');
      console.log(e);
    }
  }
Run Code Online (Sandbox Code Playgroud)

现在假设对于上面的代码,A 和 C 都失败了,我想重试它们,比如说——再试一次。即使我有a, b, c,我也只知道哪些是fullfilled:true,哪些不是。但我不知道如何链接asomethingA()和仅csomethingC()试这两个函数,而且我绝对不想调用somethingB()两次。

有任何想法吗?

Jef*_*ica 7

Promise 不会保留任何其来源的记录:一旦有了 Promise,就没有什么可以让您多次重试其来源。因此,常见的 Promise 重试模式接受 Promise 返回函数(“promise 工厂”),而不仅仅是 Promise 本身。在调用or之前将每个单独的 Promise 包装在重试函数中是最实用的解决方案allallSettled,因为快速失败的 Promise 可以立即重试,而无需像那样等待整个列表allSettled

const multiAsync = [
  retry(() => this.somethingA(), 3),
  retry(() => this.somethingB(), 3),
  retry(() => this.somethingC(), 3),
];
const [a, b, c] = await Promise.allSettled(multiAsync);

// or

const [a, b, c] =
    await Promise.allSettled([/* functions */].map(x => retry(x, 3));
Run Code Online (Sandbox Code Playgroud)

但是,如果您想了解如何Promise.allSettled直接执行此操作,我这里有一个。我的解决方案不考虑超时,您可以通过Promise.race实现全局或单独添加超时。

/**
 * Resolves a series of promise factories, retrying if needed.
 *
 * @param {number} maxTryCount How many retries to perform.
 * @param {Array<() => Promise<any>>} promiseFactories Functions
 *     that return promises. These must be functions to enable retrying.
 * @return Corresponding Promise.allSettled values.
 */
async function allSettledWithRetry(maxTryCount, promiseFactories) {
  let results;
  for (let retry = 0; retry < maxTryCount; retry++) {
    let promiseArray;
    if (results) {
      // This is a retry; fold in results and new promises.
      promiseArray = results.map(
          (x, index) => x.status === "fulfilled"
            ? x.value
            : promiseFactories[index]())
    } else {
      // This is the first run; use promiseFactories only.
      promiseArray = promiseFactories.map(x => x());
    }
    results = await Promise.allSettled(promiseArray);
    // Avoid unnecessary loops, though they'd be inexpensive.
    if (results.every(x => x.status === "fulfilled")) {
      return results;
    }
  }
  return results;
}

/* test harness below */

function promiseFactory(label) {
  const succeeds = Math.random() > 0.5;
  console.log(`${label}: ${succeeds ? 'succeeds' : 'fails'}`);
  return succeeds
      ? Promise.resolve(label)
      : Promise.reject(new Error(`Error: ${label}`));
}

allSettledWithRetry(5, [
  () => promiseFactory("a"),
  () => promiseFactory("b"),
  () => promiseFactory("c"),
  () => promiseFactory("d"),
  () => promiseFactory("e"),
]).then(console.log);
Run Code Online (Sandbox Code Playgroud)