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

2

5

I have a Promise.allSettled which i use to resolve data in the database. I have an array of Promises which I run through the Promise.allSettled, then I only use the resolved ones. Is it possible to set a timeout inside the Promise.allSettled, so that if a promise has still not resolved the data after 5 seconds, it should return as rejected?

My code looks like this:

await Promise.allSettled(promises)
.then(result => result.forEach(d => {
  if (d.status === 'fulfilled') {
    data.push(d.value)
  }
}));
Bedder answered 3/12, 2020 at 10:9 Comment(0)
C
7

You could use Promise.race on the original promise and a timeout promise that rejects, e.g.:

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

...where rejectAfterDelay is something like:

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

Side note: You could use filter to filter out the rejected promises:

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

...although I think I'd refactor the "allSettled within timeout" part into a utility function, e.g.:

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");
};

then

data.push(...await fulfilledWithinTimeout(promises, 5000));
Channelize answered 3/12, 2020 at 10:14 Comment(2)
Last question related to the Side note. Can i filter the result and then get a list of the values only? So your code filters the list so only 'fulfilled' remains in the list. Now i additionally want to have a list only containing fulfilled value objects. :) thank youBedder
@Benji - You'd typically do that with a map after the filter: result => result.filter(({status}) => status === "fulfilled").map(({value}) => value)Channelize
H
0

With a custom Promise class it's easy Demo:

import CPromise from "c-promise2";

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

Such promises can handle cancellation:

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}`);
  });
Heraldic answered 3/12, 2020 at 22:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.