What's the best way to handle a list of promises in an isolated manner?

Viewed 114

I've been giving some thought at what could be the best way to deal with a batch of heavy operations with Javascript and I came up with the following:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Collect the results
for (const result of results) {
  // Isolate the failures so one doesn't break another.
  try {
    await result;
  } catch (error) {
    console.log(JSON.stringify(error));
  }
}

My understanding is that the above snippet takes as long as the longest operation, and that's as good as it's going to get AFAIK. However, is there a better or more idiomatic way of going about this problem?

I'm not really looking necessarily at node here. This could be node, Deno or browser code.

1 Answers

In your code, just pushing the promises inside the array won't start a concurrency work, but the way you will resolve them that can be made in a concurrency way. In this for loop, each item of the results array will execute synchronously one after another without any concurrency execution and the performance will be very low.

The javascript provides a way to achieve this "concurrency" execution, using the all static method of the Promise native class:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Without handling errors will be just like this
await Promise.all(results);

In the above example, all promises will be resolved, but if one fails all of them will fail too (consider see more about allSettled, if this is not what you want). To handle each promise error, you can just attach a catch block to each promise inside the array:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Handling errors will be just like this
await Promise.all(results.map(r => r.catch(error => console.log(JSON.stringify(error)))));

Note: concurrency is underquoted because nodeJS is a monothread language, so this concurrency does not happen in different threads.

Related