Explained: All You Need to Know About JavaScript Promises

Have your parents ever promised to buy you the things you wished for?

Eugene Goh
JavaScript in Plain English

--

Before you start reading this, here are some prerequisites you need to know:

Photo by Andrew Petrov on Unsplash

What is an asynchronous operation in JavaScript?

Asynchronous operation refers to an operation that allows a computer to continue executing other code while waiting for asynchronous operations to complete. With that being said, we can save our time by using an asynchronous approach and prevent our programs from coming to a halt.

A real-life example of the asynchronous operation is when your robot is helping you to do your house chores; you’re free to do anything you want in the meantime.

The same goes for computers. We use an asynchronous approach in programming too. For example, operations like making a network request to a database can be tedious and super time consuming but JavaScript has the power to execute other tasks while awaiting its completion.

Let’s dive into modern JavaScript that handles asynchronous operations using Promise .

Macrotasks in JavaScript

Before understanding Promise in JavaScript, you should understand macrotasks first. Macrotasks enable developers to perform tasks asynchronously in a synchronous way. There are a few macrotasks in JavaScript. For example, setInterval, setImmediate, and setTimeout.

But I will only go through setTimeout for the sake of this article. So, what exactly in the world does setTimeout do? setTimeout is a Node API (also known as web API) that orders tasks to be performed after a delay. setTimeout takes in a callback function and how many milliseconds you want it to delay.

For example:

Example of setTimeout( )
  • From the example above, we will complete the synchronous operation first which is console.log(“I am first”); which will push to the stack and print to the console.
Example of console.log(“I am first”);
  • Follow by console.log(“I am second”);.
Example of console.log(“I am second”);
  • Then when it comes to useMacro turn, useMacro will only execute after 1 second. This delay is performed asynchronously. This means that our program will not halt suddenly during the delay. It’s still running but it is now inside a queue. I will explain more about the queue in the picture below. Just in case you need some Math help:
millisecond to second
  • setTimeout( ) will get pushed to the stack once it is empty and the callback function will get pushed to the Web API for processing.
Example of setTimeout( )
  • Web API will send the callback into the queue and wait for completion.
Example of setTimeout( )
  • The callback function inside the queue will then get pushed into the stack after 1 second and logged I am third to the console.
Example of console.log(“I am third”)
  • In asynchronous JavaScript, there is one thing called event-loop. Event-loop’s job is to act as a middleman between queue and stack; if there’s nothing in the stack then code in the queue will get pushed to the stack and start executing.
  • Before useMacro can run, the synchronous code will run first. Finally, after all the synchronous code,useMacro will run and return I am third to the console.
  • And this is what we will get in the final result:
Example of the final result

So, what is a Promise?

A Promise is an object that represents the future outcome (success or fail) of an asynchronous operation.

A Promise consist of three states:

  • Pending: Initial state of the operation that has yet to be completed.
  • Fulfilled: Operation has completed successfully and the Promise has a resolved value.
  • Rejected: Operation has failed and the Promise has a reason for failure. Usually will result in an Error.

An example of a real-life example of a Promise:

An example of a promise

From the picture above, a Promise initial state would always be in pending mode. If it is resolved then it will have a resolved value, else if it is rejected it would have an Error for why it failed.

What does a Promise object look like?

We use the new keyword and Promise constructor method in order to create a new Promise object.

An example of a promise object
  • First, we will make a function which also known as the executor function. From the above example, anyFunction is our executor function.
  • The executor function will be receiving two functions as parameters that will dictate the future outcome.
  • resolveand rejectare two functions as parameters.
  • If specifyYourConditionHere is true, then resolve( )code will run and return ‘I resolved!’ . Then it will alter the Promise’s status from pending to fulfilled.
  • Else reject( )code will run and return ‘I rejected!’ . Then it will alter the Promise's status from pending to rejected.
  • The Promise constructor takes in a function parameter which is the executor function and starts to run the code asynchronously. The result will either be resolved or rejected.

How to use a Promise?

As the Promise's initial state would always be in pending mode, the Promise will always have a future outcome either fulfilled or rejected.

But have you asked yourself what can I do after it has finished running the Promise?

We can use .then( ) to follow up on another action. This method always returns a Promise.

An example of using promise

From the example above,

  • If a pending promise get a result of resolve which means fulfilled, then the project is done.
  • If a pending promise get a result of reject which means rejected, then it will start slacking off.

For your information, .then( ) is a higher-order function that takes two arguments to represent the callback functions. The two arguments are called handlers.

What are the two handlers?

  1. The first handler which is known as the success handler, onFulfilled . It should contain logic that indicates the Promise is resolved.
  2. The second handler which is known as the failure handler, onRejected . It should contain logic that indicates the Promise is rejected.

How do you use success/failure handlers as callback functions?

To handle a success or failure Promise when it is resolved or rejected, we can pass the success and failure handler into a callback function, then invoke it with .then( ) as in the following:

An example of success/failure handlers using callback functions

From the example above:

  • work is a Promise that with a conditional statement checks if it is resolved then it will return a resolved value which is Working on side projects else return a rejected value which is Unclear what to do .
  • Then we will define a function which are success( ) and failure( ) . Then we will print the argument that passed into it.
  • Next, we invoke work with .then( ), passing in success( ) and failure( ) function.
  • As you might know already, it will get resolved and return a value then logged to the console which is Working on side projects.

Why don’t you follow the rule of Separation of Concerns (SoC)?

Above are just the beginning, to follow the rule of Separation of Concerns (SoC), this is where .catch( ) function comes into play.

This function will only take in one argument which is onRejected and return the reason for rejection. .catch( ) achieve the same thing as .then( ) but it is only used to catch the Error .

For example:

Example of SoC

In the picture, there is a code snippet on using .catch( ) . Since reject is true then we will return Unclear what to do as a reason for rejection.

By implementing SoC, we are able to improve our code readability and making it easier for debugging when a particular part of our code gone wrong.

How to use Promise.all( ) ?

What if you have a ton of Promises? Will you wait for all the Promises to get resolve one by one? That is where Promise.all( ) comes in.

Promise.all( ) accepts an array of Promises and returns only one Promise.

  • If every Promise in the array gets resolved, then Promise.all( ) will return a Promise containing a resolved value.
  • If a single Promise inside an array rejected, the following Promises awaiting resolving will also be rejected which is also known as failing fast, then Promise.all( ) will return a Promise containing the reason for the rejection.

For example:

Example of Promise.all( )
  • From the above, we have three Promises that are waiting for completion.
  • Secondly, we use success and failure handlers as callback functions.
  • We then create a variable which stored Promise.all( ) .
  • Promise.all( ) will determine the three Promises whether it is rejected or resolved.
  • Lastly, we invoke the variable, myPromises with .then( ) to return the resolved value and .catch( ) reason for rejection.
  • Since one Promise got rejected which is example3 , then .catch( ) will do its job to catch the reason for rejection which is Failed: I rejected! .

Conclusion

That all for Promises in JavaScript. Remember, a Promise is something that will either resolve or reject — it will always be in pending mode. Have you learned anything from this article? I hope you have found this article useful, thank you for reading.

Get connected with me via LinkedIn and Twitter.

More content at plainenglish.io

--

--

17-years-old purpose-driven software engineer & student. Sharing knowledge of what I have learned.