与多个映射函数调用一起使用时,Nodejs Promise.all 不会捕获错误/承诺拒绝

问题描述

我使用 Promise.all 来运行两个单独的函数,它们本身就是在 Promise.all 中多次运行一个函数的映射。

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

当没有抛出错误时,我的解决方案可以正常工作。但是,如果抛出错误,Promise.all 的捕获不会捕获错误,而是传播到主调用应用程序。

在这代码中,第二个函数调用 iterateArrayAndCall(arr2) 抛出了一个错误。但它不会被 catch 上的 Promise.all 捕获。

任何帮助将不胜感激。完整代码如下...

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,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`);
}

运行时输出


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

解决方法

可能您的 iterateArrayAndCall 直接在其自己的堆栈(而不是异步堆栈)上抛出,因此错误由其底层堆栈 (nonBlockingWithMapAndArray) 捕获:

function iterateArrayAndCall () { throw Error() }

Buf 如果你将调用包装到一个 promise 中,它将被 .catch 块捕获:

function iterateArrayAndCall () { 
  return new Promise((resolve,reject) => { throw Error() })
}