0

I have a unique situation where 2 promises are running together in Promise.all. but one of the promise is taking long time and hence I am not getting any result. Other promise are getting resolved except one. I want to reject the promise taking long time (eg: if more than 60 sec) with an error message so that I can get a response from Promise.all.

e.g::

const [client1Prices, client2Prices] = await Promise.all([
      this.client1.getSimulationPrices({
        hourPay: journey.hourPay,
        jobType: journey.jobType,
        salary: journey.salary,
        weeklyHours: journey.weeklyHours,
      }),
      this.client2.getSimulationPrices({   // takes more than 60 sec and i want to reject this promise
        hourPay: journey.hourPay,
        jobType: journey.jobType,
        salary: journey.salary,
        weeklyHours: journey.weeklyHours,
      })
    ]);

this.client2.getSimulationPrices is taking a lot of time to resolve and hence Promise.all is not giving me any result. I want to reject this in 60 second such thatI can get response from Promise.all.

Please suggest how can this situation be handled ?

4 Answers4

2

Use Promise.allSettled()

It return ar array of objects promise that resolves after all of the given promises have either fulfilled or rejected.

Each objects in the array describes the outcome of each promise.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Prmoise 2'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'Promise 3'));

const promise4 = new Promise((resolve, reject) => setTimeout(reject, 200, 'Promise 4'));

const promises = [promise1, promise2, promise3, promise4];

Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));

// expected output:

// "fulfilled"
// "rejected"
// "fulfilled"
// "rejected"

More info

Promise.allSettled() - MDN


All major browsers support it. So you do not need to use any external library or even polyfills.

mahan
  • 12,366
  • 5
  • 48
  • 83
0

You are looking for Promise.allSettled(). Unlike Promise.all() it would not thrown an error if any of the promises fails. Instead the result is an object that is either:

{
    status: 'fulfilled',
    value: return_value
}

if there was no error or:

{
    status: 'rejected',
    reason: error_message
}

if there was an error.

MDN says this is supported from node 12.9.0 and above. If you need it in an environment that does not have it there is a pure-js implementation on npm: https://www.npmjs.com/package/promise.allsettled

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

slebetman
  • 109,858
  • 19
  • 140
  • 171
  • 1
    This does not address the timeout concern. If the promise takes longer than 60sec (success or fail), `Promise.allSettled()` will still wait too long to resolve. – ChrisG Nov 05 '20 at 19:27
0

You should consider using observables as this is a bit easier to handle, however, you can still do this with promises.

You'll want to use Promise.race().

The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.

const timeoutPromise = new Promise((resolve, reject) => setTimeout(() => reject('TIMEOUT'), 60000));
});
const [client1Prices, client2Prices] = await Promise.all([
      this.client1.getSimulationPrices({
        hourPay: journey.hourPay,
        jobType: journey.jobType,
        salary: journey.salary,
        weeklyHours: journey.weeklyHours,
      }),
      Promise.race([timeoutPromise, this.client2.getSimulationPrices({
        hourPay: journey.hourPay,
        jobType: journey.jobType,
        salary: journey.salary,
        weeklyHours: journey.weeklyHours,
      })])
    ]);

You are guaranteed to have client2Prices return in 60sec or less. You can check the value to determine if it timed out or if it succeeded. If you wanted to swallow the timeout error (not have Promise.all() fail, you can either make the timeout resolve (instead of reject), or switch to use Promise.allSettled()

ChrisG
  • 2,637
  • 18
  • 20
0

An excellent library for Promises is bluebird. It has a timeout function that would prove useful here.

const Promise = require('bluebird');

const [client1Prices, client2Prices] = await Promise.allSettled([
      this.client1.getSimulationPrices(...).timeout(60000),
      this.client2.getSimulationPrices(...).timeout(60000)
    ]);
jeeves
  • 1,871
  • 9
  • 25