Introducing PromiViz - visualize and learn JavaScript promise APIs

Introducing PromiViz - visualize and learn JavaScript promise APIs

A picture is worth of thousand words. Millions of pictures come together to make visuals. Let's visually learn about promise APIs. Please read on.

Why does JavaScript promise sounds a bit more complex than many other topics? Besides the factors that we have already discussed, we also need to know how it executes, what's the background story? After spending a considerable amount of time practicing and thinking about the promises using pen and paper, I got an idea to make a simple yet powerful tool for developers.

Meet PromiViz

PromiViz

Promiviz is an open-source tool to try out the promise methods in intuitive ways. You can configure promises with delays, rejections and run them to see what's exactly happening behind the scene. It captures the log of each of the operations so that your understanding gets firmed as you use it. It is a tool for developers by a developer!

Please check out this short video to know more about the tool.

Here are the important links:

In this article, we will learn the Promise API methods using the PromiViz tool.

JavaScript Promise APIs

The Promise object in JavaScript has six practical methods that serve several use cases.

  1. Promise.all
  2. Promise.any
  3. Promise.race
  4. Promise.allSettled
  5. Promise.resolve
  6. Promise.reject

These methods take one or more promises as input and return a new promise to find the result or error. The first four methods are significant when it comes to handling multiple promises.

To demonstrate examples for each of these methods, we will use three promises. Each of these promises resolves with a color name, red, green, and blue respectively,

// It resolves with the value red after 1 second 
const red = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('red');
    }, 1000);
});

// It resolves with the value green after 3 seconds
const green = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('green');
    }, 3000);
});

// It resolves with the value blue after 5 seconds
const blue = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('blue');
    }, 5000);
});

Promise.all

The method Promise.all executes multiple promises in parallel and returns a new promise. It waits for the execution of all the premises to complete. So, the execution time of the Promise.all method will be the same as the max time taken by an input promise.

Let's use our example promises(red, green, and blue) to explain the Promise.all method.

const testAll = async () => {
    const colors = await Promise.all([red, green, blue]);
    console.log(colors);
    colors.forEach(color => {
        console.log(color);
    });
}

testAll();

Here we use the async/await keywords. As the Promise.all method returns a new promise, we use the await keyword in front of it. By rule, we must use an async keyword for a function that uses await in it.

The variable colors is an array with all the resolved values,

image.png

A few points to consider here,

  • The total time needs to execute the Promise.all method is 5 seconds. The blue promise takes the max time(5 secs) to complete.
  • The resulting array has the resolved value in the same order of the promises passed to the Promise.all method.
  • If any of the input promises reject(or error out), the Promise.all rejects immediately. It means the rest of the input promises do not execute.

Let's try these with Promiviz. First, execute the Promise.all API and observe the output in the log window.

promise all resolve

Have a look at the execution time there. It took 5 seconds. That is the time the blue promise took to finish. Now let's reject a promise, say, the green one!

promise all reject

Again, look at the time in the log window. The Promise.all is rejected within 3 seconds(the time green takes to execute). It didn't even wait for the blue promise to execute.

Frequent mistake: All the input promises run parallel with the Promise.all method. Hence the total time to execute all the promises successfully is NOT the sum of their time. It is the max time taken by an input promise. In our example, it is 5 seconds(time taken by blue), not 9 seconds(1 + 3 + 5).

Let's move onto the following promise API method.

Promise.any

Similar to Promise.all, the any method also takes a collection of input promises. However, it returns a new promise when any of the input promises is fulfilled.

const testAny = async () => {
    const color = await Promise.any([red, green, blue]);
    console.log(color);
}

testAny();

In this case, the first promise, red takes the least time to execute and resolves. Hence the output will be red.

A few points to consider,

  • If any of the input promises are rejects or errors out, the Promise.any method continues to execute other promises.
  • If all of the input promises reject, the Promise.any method rejects with AggregateError.

Let's try these using PromiViz. Select the Promise.any API method and observe the log window.

promise any red

The API method took 1 second to execute the red promise and resolves with it. What happens when you reject the red promise. Let's do it.

promise any green

Now, the green promise resolves as it is the next one to pick. If we now reject red and green, the API will resolve the last input promise, blue. Let us now reject all the promises and see what happens.

promise any reject

It is AggregateError. Notice the time taken to execute, and it is 5 seconds, the max time taken by an input promise(blue).

Promise.race

As the name suggests, it is the race between all the input promises, and the fastest promise wins! The Promise.race API method accepts a collection of input promises and returns a new promise when the fastest promise resolves.

const testRace = async () => {
    const color = await Promise.race([red, green, blue]);
    console.log(color);
}

testRace();

In our example, the red promise is the clear winner. It resolves within 1 second.

A point to consider,

  • If the fastest promise rejects(or error out), the Promise.race API method returns a rejected promise. It is a fundamental difference between the race method with the any method. The any method keeps trying, whereas the race is all about making the fastest win else all lost.

Let's understand it using PromiViz. Would you please run the Promise.race API method? We see red wins the race in 1 second.

race red wiins

Now adjust the delays. Make it 3 seconds for red, 2 seconds for green. You should see the green winning the race now as it is the fastest.

race green wins

Now reject green. What do you think will happen? You have rejected the fastest promise. So, by rule, the Promise.race will not continue the execution of others. We will get a rejected promise that we need to handle.

race reject fastest

Alright, let's move onto the following important API method.

Promise.allSettled

The Promise.allSettled method is the newest inclusion to the promise API method list. Just like the methods we have seen so far, it takes an array of input promises.

Unlike the Promise.all method, it doesn't reject all if any input promises reject or error out. It continues to execute and returns an array of settled promises, including their state, value, and the reason for an error.

Let's assume the red and green promises resolves successfully and the blue promise rejects due to an error. Let's run Promise.allSettled using these promises,

const testAllSettled = async () => {
    const colors = await Promise.allSettled([red, green, blue]);
    console.log(colors);
    colors.forEach(color => {
        console.log(color);
    });
}

See the output,

promise all settled

It returns all the settled promises with status, value for a resolved promise, and reason for the rejection for a rejected promise. Here is the execution result of the Promise.allSettled API method using PromiViz. Please note, we reject the blue promise here.

all-settles-pv.png

It took the entire 5 seconds to complete execution. It never rejected the other two promises.

Promise.resolve and Promise.reject

The last two methods are Promise.resolve and Promise.reject. The first creates a resolved promise with a value, and the latter creates a rejected promise with an error.

// It resolves with the value green after 3 seconds
const green = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('green');
    }, 3000);
});

const resolveOne = async () => {
    const result = await Promise.resolve(green);
    console.log(result);
}

resolveOne();

In most cases, you would probably prefer using async/await instead of these two methods. However, consider these methods when you create promises manually like this,

new Promise(function (resolve, reject) {
  resolve(value);
}).then(/* handle it */);

The better and short syntax is,

Promise.resolve(value).then(/* handle it */);

Similarly, for reject,

Promise.reject(value).catch(/* handle it */);

Congratulations!!! You have learned about all the Promise API methods.

Examples and Analogies

Here are some examples and analogies you may find helpful.

Promise API MethodsExample
Promise.allI am downloading multiple files from different sources.
Promise.allSettledI am downloading multiple files from different sources, and I am okay with whatever was downloaded successfully.
Promise.anyI am downloading my profile image of different resolutions from several sources. I am OK with any that I get first.
Promise.raceI am downloading my profile images of different resolutions from several sources. I want to get the fastest one to proceed.

So, What's Next?

We have come a long way in understanding the core concepts of asynchronous programming in JavaScript. To recap, we learned about,

Thank you for letting me know, you are enjoying the series so far. Next, we will learn about the common mistakes we make with promises and get better at answering the interview questions. Until then, you can look into the source code used in the article from this repository and try it out using PomiViz.


I hope you enjoyed this article or found it helpful. Let's connect. Please find me on Twitter(@tapasadhikary), sharing thoughts, tips, and code practices. Would you please give a follow? You can hit the Subscribe button at the top of the page to get an email notification on my latest posts.

Did you find this article valuable?

Support GreenRoots Blog - Tapas Adhikary by becoming a sponsor. Any amount is appreciated!