Home > Software design >  Force a controlled rejection in promise.allSettled()
Force a controlled rejection in promise.allSettled()

Time:11-16

I am trying to force a rejection for a promise.allSettled() function in a controlled way. The idea is run a series of urls in batches through an API, this api from time to time returns a 500 error for a given request and can safetly be retried. So I want to trigger a rejection on promise.allSettled() where I can collect the failing urls and later on rerun on a recursion.

Batchrequest function

export async function batchRequest(poolLimit, array, iteratorFn, exception) {
  const promises = []
  const racers = new Set()

  for (const item of array) {
    const pro = Promise.resolve().then(() => iteratorFn(item, array))
    promises.push(pro)
    racers.add(pro)
    const clean = () => racers.delete(pro)
    pro.then(clean).catch(clean)

    if (racers.size >= poolLimit) await Promise.race(racers)
  }


  const results = await Promise.allSettled(promises)

  // Collect errors rejected by iteratorFn,
  const rejected = results
    .filter(({ status, reason }) => status === 'rejected' && reason.name === exception)
    .map(({ reason }) => reason.error)

  // Recurse the array of rejected urls
  if (rejected.length) {
    await batchRequest(poolLimit, rejected, iteratorFn, exception)
  }
}

Here we run the promises as normal but collect all rejected urls, I am trying to use the exception 'timeout' as the rule to determine if it needs to be rerun as it was just a timeout error.

Iterator function

async function runRequest(url) {
  try {
    const { data } = await axios('https://exampleAPI.com')
    // Take the data and write it somewhere...
  
  } catch (error) {

    if (error.response.status === 500) {
      throw { name: 'timeout', url }
    }
  }
})

const urls = [...many urls]
await batchRequest(100, urls, runRequest, 'timeout')

I am getting an error saying

This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<Object>".] { code: 'ERR_UNHANDLED_REJECTION' }

How can I force a controlled rejection on promise.allSettled()?

UPDATE-----

I found that the unhandled rejection was at the point in which I started the batchrequest

await batchRequest(100, urls, runRequest, 'timeout')

I needs a try catch there, but the whole point was to use the promise.allSettled() to absorb the error and not get out of the batchrequest

CodePudding user response:

It looks to me like you're not handling the exception thrown by the runRequest or the first call to iteratorFn. That's what's causing the "unhandled exception" error.

I'd suggest to run this code through a debugger and go line by line until you find the line causing you to get that exception thrown.

CodePudding user response:

Just manually handle each promise and collect errors and results and return or throw when all promises resolved:

const errors = [];
const results = [];

Promise.all(
  promises.map(p => p.then(r => results.push(r)).catch(e => errors.push(e))
).then(() => {
  if (errors.length) throw errors;

  return results;
});
  • Related