DEV Community

Cover image for The Power of Javascript Promise.all()
Geoffrey Bourne
Geoffrey Bourne

Posted on • Edited on • Originally published at ayrshare.com

The Power of Javascript Promise.all()

This post is not about Promises, async/await, or the single-threaded nature of Javascript. If you want to know about these topics, go here, here, or here.

This article is about the function Promise.all() and how you bring your independently running functions or tasks together into a beautiful result.

As is often the case, an example or use case does wonders.

Use Case: Post to Multiple Social Media Networks

Our app Ayrshare’s primary purpose is to provide one API to post across multiple social media networks, such as Twitter, Instagram, Facebook, and LinkedIn.

A user will make an API call (RESTful or via a client package) with the post text, image, and the list of networks to send the post. The API returns the resulting success or error for each network as an array of results.

However, there is complexity behind the scenes.

Each network has its own authorization, workflow, and security requirements. For example, if you attach an image to the post, Twitter requires you to first upload the image, wait for their processing to complete, and send the returned media ID in your a new update status call to Twitter. Timings vary, errors can occur for some and not for others, and new networks are added all the time. But from an end-user perspective, they sent the post and the results are returned right away.

Here is a visual (Firebase tech stack overview):

Ayshare Tech Stack

We want to run all the network posts in parallel and collect the results to return to the caller. As the number of networks increases, we ideally want to take no longer than the longest network.

The problem we face is if each network function call is asynchronous, then each function will complete at different times and we won’t be able to return the results in one response.

Promise.all() To The Rescue

If you have a similar problem, Promise.all() is your solution.

Promise.all() “takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises.”

In other words, if you call several async functions, you can wait until they all resolve and capture their output in an array.

Let’s see a simple example:

const getSquare = async (x) => Math.pow(x, 2);
const printSquares = async () => {
     const nums = [1, 2, 3, 4, 5];
     const promiseArray = nums.map(x => getSquare(x));

     console.log(promiseArray);
};

printSquares();
Enter fullscreen mode Exit fullscreen mode

printSquare iterates over an array of ints and squares each one. Calling getSquare returns a promise because we made it an asynchronous function by adding the keyword async – technically we don’t need async since the Math.pow function is synchronous.

The promiseArray is an array of promises, waiting to be resolved. When we print the promiseArray:

[
   Promise { 1 },
   Promise { 4 },
   Promise { 9 },
   Promise { 16 },
   Promise { 25 }
]
Enter fullscreen mode Exit fullscreen mode

Close, but what is with those “Promises”? Well, as mentioned above, the array is composed of Promises waiting to be resolved. They can easily be resolved with Promise.all():

const getSquare = async (x) => Math.pow(x, 2);
const printSquares = async () => {
     const nums = [1, 2, 3, 4, 5];
     const promiseArray = nums.map(x => getSquare(x));

     const results = await Promise.all(promiseArray);
     console.log(results);
};

printSquares();
Enter fullscreen mode Exit fullscreen mode

and the results:

[ 1, 4, 9, 16, 25 ]
Enter fullscreen mode Exit fullscreen mode

Perfect! Also the results order is maintained.

Notice that we need to await on Promise.all() since itself returns a Promise that needs to be resolved.

For those that are curious, here is a real example of Ayrshare’s code that posts to all the networks:

/**
* Post to social networks
*/
const postNetworks = NETWORKS.map((net) => {
  return platforms.includes(net.type)
    ? net.func(auth[net.type], post, urls, id, {...})
    : [];
});

const processedSocial = await Promise.all(postNetworks);
Enter fullscreen mode Exit fullscreen mode

Final Thoughts: Parallel Processing

Often Promise.all() is thought of as running in parallel, but this isn’t the case.

Parallel means that you do many things at the same time on multiple threads.

However, Javascript is single threaded with one call stack and one memory heap. It is an asynchronous, non-blocking language. This means Javascript doesn’t run in parallel, but rather runs only one function/Promise at a time. If the single thread has to wait on something, like the return from an http call, it will move on to another function until the return is complete.

In the case of our array of Promises, each Promise will be handled one at at time, but Javascript will switch between each one if the processing needs to wait. While the order of Promise resolution in Promise.all() can vary depending upon blocking, the final result will be an array of ordered results.

Post to Social Media via an API

If you're interested in posting to social media for your company or on behalf of your clients via an API, dive straight into the API Docs.

Originally published at Ayrshare

Top comments (2)

Collapse
 
leandroandrade profile image
Leandro Andrade

Hi! Thanks for your post. In your solution, think about use Promise.allsettled instead of Promise.all.

Promise.allsettled allow you handle errors if some requests return it.

Collapse
 
gbourne profile image
Geoffrey Bourne

Good one. Promise.allSettled is might be better in some circumstances to handle rejected Promises.