Search code examples
javascriptnode.jspromiseevent-looppromise.all

executing multiple await promise.All not catching the error correctly


im trying to execute multiple pormises in chunks using promise all => this is just a code example - throwing every time getting the number 72. i have a simple catch in the function that calls executePromises but it does not catch as it should. the problem is that even if the first promise chunk fails the second one is also executed. so, all the promises execute and it the fail happens in 2 chunks i get an unhandled exception. if only 1 chunk fails then the catch is correct and it works fine. in debug, even if a promise is rejected it continue to run the chunks - i think its because they run sort of in "parallel", but if there are 2 errors in the same chunk it does not throw twice so makes sense.

i tried also using a .catch for the promise all but it didnt help either , the second chunk does not get there. also tried throwing an error inside the same catch also didnt help.

class testClass{  
async test(num: number): Promise<{ failed_skus: { id: string }[] }> {
  return new Promise((resolve, reject) => {
  setTimeout(() => {
    if (num === 72) {
      reject(new Error(`index : ${index}, num : ${num}`));
      index++;
    } else {
      resolve({ failed_skus: [{ id: 'str' }] });
      index++;
    }
  }, 1300);
});
}

async executePromises(): Promise<void> {

index = 0;
const productUpdateRequests = [
  72,
  900,
  3,
  4,
  5,
  6,
  72,
  800,
  9,
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  9,
  1,
  2,
  3,
  4,
  5,
  6,
  72,
  100,
  9,
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  9,
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  9,
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8,
  9,
];

const productsUpdatesRequestsPromises = productUpdateRequests.map(this.test.bind(this));

const requestChunks = chunk(
  productsUpdatesRequestsPromises,
  20,
);
for (const requestChunk of requestChunks) {
  const updateStockResponses = await Promise.all(requestChunk);
}

} }


Solution

  • The binding and setup of the promises was confusing me. Here is a working version of your code.

    You probably needed this.index in your bound test() function (not just 'index'). I did not make it a class variable as, due to NodeJS timing, I'm not sure you could have guaranteed the order would match the array. My version just uses the array index.

    Also, I found that mapping all the test functions at once started running them all at once, essentially finishing the program after 1.3s. In my code I only started the test function for the chunk I was on, so each chunk takes 1.3s.

    Oh, and I didn't see a chunk() function, so I made one.

    I hope you find this helpful. Happy coding!

    function chunk(array, chunkSize) {
      let chunks = []
      for (let i = 0; i < array.length; i += chunkSize) {
        chunks.push(array.slice(i, i + chunkSize))
      }
      return chunks
    }
    class testClass {
      async test(num, idx) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (num === 72) {
              reject(new Error(`index : ${idx}, num : ${num}`))
            } else {
              resolve({ failed_skus: [{ id: 'str' }] })
            }
          }, 1300)
        })
      }
    
      async executePromises() {
        let processedChunks = 1
        const productUpdateRequests = [
          72, 900, 3, 4, 5, 6, 72, 800, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 72, 100, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3,
          4, 5, 6, 7, 8, 9,
        ]
    
        const requestChunks = chunk(productUpdateRequests, 20)
        for (const requestChunk of requestChunks) {
          const updateStockResponses = await Promise.all(requestChunk.map((num, idx) => this.test(num, idx)))
            .then(() => {
              console.log('Finished processing chunk ' + processedChunks)
            })
            .catch((error) => {
              //console.log(error)
              console.log('Error processing chunk ' + processedChunks)
            })
          processedChunks++
        }
      }
    }
    
    let c = new testClass()
    c.executePromises()