如果解决时间超过 5 秒,是否可以拒绝 Promise.allSettled 中的每个 Promise?

Is it possible to reject each Promise in Promise.allSettled if it takes more than 5 seconds to resolve?

我有一个 Promise.allSettled 用来解析数据库中的数据。我有一个 Promise 数组,我 运行 到 Promise.allSettled,然后我只使用已解决的 Promise。是否可以在 Promise.allSettled 内设置超时,以便如果 promise 在 5 秒后仍未解析数据,它应该 return 被拒绝?

我的代码如下所示:

await Promise.allSettled(promises)
.then(result => result.forEach(d => {
  if (d.status === 'fulfilled') {
    data.push(d.value)
  }
}));

您可以在原始承诺和拒绝的超时承诺上使用 Promise.race,例如:

await Promise.allSettled(
    promises.map(promise => Promise.race([promise, rejectAfterDelay(5000)])) // **
)
.then(result => result.forEach(d => {
  if (d.status === 'fulfilled') {
    data.push(d.value)
  }
}));

...其中 rejectAfterDelay 类似于:

const rejectAfterDelay = ms => new Promise((_, reject) => {
    setTimeout(reject, ms, new Error("timeout"));
};

旁注:您可以使用 filter 过滤掉被拒绝的承诺:

data.push(...
    await Promise.allSettled(
        promises.map(promise => Promise.race([promise, rejectAfterDelay(5000)]))
    ).then(result => result.filter(({status}) => status === "fulfilled"))
);

...尽管我认为我会将“超时内的allSettled”部分重构为实用函数,例如:

const fulfilledWithinTimeout = async (promises, timeout) => {
    promises = Array.isArray(promises) ? promises : [...promises];
    const all = await Promise.allSettled(promises.map(promise => Promise.race([promise, rejectAfterDelay(timeout)]));
    return all.filter(({status}) => status === "fulfilled");
};

然后

data.push(...await fulfilledWithinTimeout(promises, 5000));

使用自定义 Promise class 很简单 Demo:

import CPromise from "c-promise2";

CPromise.allSettled([...promises])
  .timeout(1000)
  .catch((err) => {
    console.warn(`Fail: ${err}`); // Fail: CanceledError: timeout 
  });

此类承诺可以处理取消:

import CPromise from "c-promise2";

CPromise.allSettled([
  new CPromise((resolve, reject, { onCancel }) => {
    const timer = setTimeout(resolve, 2000);
    onCancel(() => {
      clearTimeout(timer);
      console.log("Wow! timeout cleared");
    });
  })
])
  .timeout(1000)
  .catch((err) => {
    console.warn(`Fail: ${err}`);
  });