0

I am using Promise.all to run two separate functions which in themselves are maps running a function within a Promise.all multiple times.

async function nonBlockingWithMapAndArray(): Promise<any> {
  let arr = [100,200,150];
  let arr2 = [500,1000,300]; // <--- value of 1000 causes an error to be thrown
  console.log(`nonBlockingWithMapAndArray: starting...`);
  const pa = await Promise.all([ 
    await iterateArrayAndCall(arr),
    await iterateArrayAndCall(arr2)    // <--- This throws an error
  ])
  .then( (data) => {
     console.log(`nonBlockingWithMapAndArray: In then...`);
  })
  .catch((err) => {     // <-- This should catch the error but does not, instead the error goes up to main which calls nonBlockingWithMapAndArray()
    console.log('nonBlockingWithMapAndArray: In catch', err);
  });

  console.log(`nonBlockingWithMapAndArray: Finished`);
  return pa;
}

The solution I have works correctly when no errors are thrown. However, if an error is thrown, the catch of the Promise.all does not catch the error and instead it propagates up to the main calling app.

In this code, the 2nd function call iterateArrayAndCall(arr2) throws an error. But it is not caught by the catch on the Promise.all.

Any help would be appreciated. Full code below...

bootstrap();

async function bootstrap() {
  let res;
  try {
    res = await nonBlockingWithMapAndArray();
  } catch (err) {
    console.log('Main: In catch');   // <-- This is where the error is caught
  }
}

async function nonBlockingWithMapAndArray(): Promise<any> {
  let arr = [100,200,150];
  let arr2 = [500,1000,300]; // <--- value of 1000 throws an error
  console.log(`nonBlockingWithMapAndArray: starting...`);
  const pa = await Promise.all([ 
    await iterateArrayAndCall(arr),
    await iterateArrayAndCall(arr2)    // <--- This throws an error
  ])
  .then( (data) => {
     console.log(`nonBlockingWithMapAndArray: In then...`);
  })
  .catch((err) => {     // <-- This should catch the error but does not
    console.log('nonBlockingWithMapAndArray: In catch', err);
  });

  console.log(`nonBlockingWithMapAndArray: Finished`);
  return pa;
}

async function iterateArrayAndCall(arr: any) : Promise<any> {
  return await Promise.all(
    arr.map( async (element) => {
      await delayAndGetRandomPromWithError(element); //If element is 1000 an error will be generated
    })
  )
  .then((data) => {
     console.log(`iterateArrayAndCall: in then...`);
  })
  .catch((err) => {
    console.log(`iterateArrayAndCall: in catch`);
    throw new Error('Failed in iterateArrayAndCall');
    // Also tried:
    // return Promise.reject();
    // return err
  });

}

async function delayAndGetRandomPromWithError(ms): Promise<number> {
  console.log(`MS start :${ms}`);

  if( ms === 1000 ) {
    throw new Error('1000 so throw error...');
  }

  const p: Promise<number> = new Promise(resolve => 
    setTimeout(() => {
      const val: number = Math.trunc(Math.random() * 100);
      console.log(`MS finish :${ms}`);
      resolve(val);
    }, ms
  ));
  return p;
};

async function throwOne() {
  console.log(`Am I blocking?`);
  throw new Error(`Test error`);
}

Output when run


nonBlockingWithMapAndArray: starting...
MS start :100
MS start :200
MS start :150
MS finish :100
MS finish :150
MS finish :200
iterateArrayAndCall: in then...
MS start :500
MS start :1000
MS start :300
iterateArrayAndCall: in catch
Main: In catch   <--- I expect to see this here instead .... 'nonBlockingWithMapAndArray: In catch'
MS finish :300
MS finish :500
iamonkey
  • 121
  • 1
  • 4
  • You're awaiting the promises, then trying to put the *result* in the array you pass to Promise.all... – jonrsharpe Feb 04 '21 at 08:09
  • Hey Jon, thanks for the feedback. Using one of the three solutions I tried, the ```return err``` in ```iterateArrayAndCall``` puts the error into the return value. The issue now is that no error is thrown, the Promise.All in ```iterateArrayAndCall``` resolves and the ```then``` is called in ```nonBlockingWithMapAndArray```, rather than the catch. – iamonkey Feb 05 '21 at 05:30

1 Answers1

-1

Probably your iterateArrayAndCall throws directly on its own stack (instead of async stack), so the error is caught by its underlying stack (nonBlockingWithMapAndArray) :

function iterateArrayAndCall () { throw Error() }

Buf if you wrap the call into a promise, it will be caught by the .catch block:

function iterateArrayAndCall () { 
  return new Promise((resolve, reject) => { throw Error() })
}
clarkttfu
  • 577
  • 6
  • 11
  • Thanks for your input clarkttfu. I tried that and it has the same effect as ```throw new Error('Failed in iterateArrayAndCall');``` ... i.e. the main function catches it – iamonkey Feb 05 '21 at 05:24