DEV Community

Cover image for Parallel promise execution: Beyound Promise.all()
abdellah ht
abdellah ht

Posted on

Parallel promise execution: Beyound Promise.all()

In this post, I will talk about running promises in parallel and optimizing for the lowest waiting time possible.

When you have a bunch of promises that need to execute sequentially, you
just run them in a sequence usein .then() or awaits:

getUser()
    .then((user) => getPosts(user.id))
    .then((posts) => use(posts));

// Or

let user = await getUser();
let posts = await getPosts(user.id);
use(posts);
Enter fullscreen mode Exit fullscreen mode

But when they don't depend on each other like the posts that need the user id,
you can run them in parallel using Promise.all().

let [value1, value2, valueN] = await Promise.all([promise1, promise2, promiseN]);
Enter fullscreen mode Exit fullscreen mode

Which accepts an array of an arbitrary number of promises and returns a promise with all the valuse
once each of them is resolved.

If one of them fails, the whole things rejects with the first error that occured.

One possible solution to prevent the whole thing from failing would be to chain the promiseN in
the array with a .catch() which will make it always resolve no matter. The problem that arrises
is how to detect if the valueN we get on the other side is from its resolution or rejection.

A possible solution would be to always return a tuple or an object from these promises.

Example:

const wrap = (promise) => promise.then((value) => ({ error: null, value })).catch((error) => ({ error }));
Enter fullscreen mode Exit fullscreen mode

With this utility in hand, we can inspect the valueN on the other side to see if it was a success or not,
and there's still room for sophisticaiton to return more metadata for retrying purposes.

const wrappedPromises = [promise1, promise2, promiseN].map((promise) => wrap(promise));
let values = await Promise.all(wrappedPromises);
Enter fullscreen mode Exit fullscreen mode

What about when the array of promises is huge (by whatever definition makes sense for your application),
maybe you're running some resource intensive processes and don't want to exceed 10 at a time?

The answer is chunking, you could use something like lodash.chunk or roll your own.

let chunks = _.chunk(arrayOfPromises, 10);

for (let chunk of chunks) {
    let chunkValues = await Promise.all(chunk.map(wrap));
    use(chunkValues);
}
Enter fullscreen mode Exit fullscreen mode

This was part of a recent optimization job I did for a client, I hope you found it as helpful as I did.

If you enjoyed this post, don't forget like and follow for more.

:wq

Top comments (0)