Javascript Promise: A Beginner’s Guide to Asynchronous Programming

Promises are a powerful feature of JavaScript that allows us to handle asynchronous operations in a more organized and efficient way.

A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It allows us to write code that is non-blocking and handles asynchronous results more elegantly than with traditional callbacks.

Let’s see the basic structure of a promise first.

1)- Promise: Basic structure

As a promise is an object, to create a new promise we use the keyword new. By calling new Promise(…), we create a new promise instance.

>

The new instance of the promise will contain 2 arguments, and they are both functions called the executor functions.

-->

The arguments are: resolve and reject.

pub-ad-placeholder-121" data-inserter-version="2">
  • resolve: It is a function provided by the promise constructor. After running the code, if the code does not encounter any errors or problems, then the asynchronous operation is completed successfully and the results of this asynchronous operation are passed to the resolve function.
  • reject: It is also a function provided by the promise constructor. When called, it rejects the promise with a reason, indicating that the asynchronous operation has encountered an error or failure. The rejection reason is passed to the reject function.

Below is an example of a promise:

JavaScript
var results = new Promise( (resolve,reject)=>{

            setTimeout(()=>{

                var randomNumber=Math.random();

                if (randomNumber<0.5) {
                
                    resolve("Amazing, this number " + randomNumber + " is less that 0.5");
                    
                }else{
                
                    reject("No, this number " + randomNumber + " is too high");
                }
                
            })
            
        }, 2000);

        results.then((data)=>console.log(data)).catch((e)=>console.log(e));

In this example, we create a promise called results. Inside the promise’s executor function, we simulate an asynchronous operation using a timeout. After a delay of 2000 milliseconds, we generate a random number.

If the random number is less than 0.5, we consider the operation successful and call the resolve function, passing the random number as the result of the promise. If the random number is greater than or equal to 0.5, we consider the operation a failure and call the reject function.

Now to display the data or the results on the browser the promise has its own methods: .then() method and .catch() method.

.then() method is used to specify what to do when the promise is resolved successfully. Which means what do we want to do with the results? Do we want to display it or to modify it …? In this case, we log the resolved result to the console.

.catch() method is used to handle any errors that occur during the promise’s execution. If the promise is rejected, the error message is logged to the console.

When you run this code, it will create a promise, wait for 2 seconds, and then either resolve or reject based on the random number generated. The corresponding .then() or .catch() block will execute accordingly.

Now that we know the basic structure of a promise, let’s see why a promise is often used for asynchronous operations and considered better than callbacks. So Let’s see the difference between a code written with callbacks and a code written using promises.

To know more about callbacks you can refer to this tutorial: How to Use Callback Functions in JavaScript: A Complete Guide with Examples and Best Practices

2)- Code written using callbacks

Let’s take the following asynchronous example where we have a simple function loginUser that will ask the user for its email and password to login:

JavaScript
function loginUser(email , password ){
 
            setTimeout( ()=>{
            
                return {userEmail : email};
                
            } , 5000)
        }

This function will return the user’s email after 5000 ms, which means 5 seconds, using the setTimeout() function.

To display the results on the browser we have to invoke the function loginUser like this:

JavaScript
function loginUser(email,password){

            setTimeout(()=>{
            
                return {userEmail:email};
                 
            }, 5000)
        }

        var user = loginUser( "john@gmail.com" , 32654);
        
        console.log (user); 
        
        //undefined

However, when we try to console.log() the user variable we get undefined as a result. Why? Simply because the console.log() syntax is synchronous, and Javascript executes the lines of the code one after another. So it will read the first line of the code which is function loginUser() , then it will put the result inside the user variable, then it will console.log() the result. However here the result will be returned after 5 seconds, not right away. That’s why we need to tell Javascript to wait a little bit for the result to be returned to display it instead of displaying it immediately, and to do that we use callback functions instead of return syntax.

So the code will be:

JavaScript
function loginUser(email,password, callback){

            setTimeout(()=>{
            
                callback ({userEmail:email});
                
            }, 5000)
        }

        var user = loginUser("john@gmail.com", 32654, (emaildisplayed)=>{
        
            console.log(emaildisplayed);
        });
        
        // {userEmail: 'john@gmail.com'}

So here we added the callback function as a third parameter of the loginUser() function, and we used the callback to display the user’s email instead of the return syntax. And we also defined the function of our callback (meaning what do we want our callback function to do: Do we want it to console.log() the result or do we want it to change the result or remove the result…) inside the loginUser() function when we invoked it.

As we want the callback to display the result only we added the function (emaildisplayed) => console.log(emaildisplayed).

Now let’s see when things get complicated when we add a callback function inside another, or what we call callback hell!

Let’s say that we want to get the user’s contacts, but to get the user’s contacts we have first to have his email. So the user’s contacts information is linked to the user’s email information.

JavaScript
function loginUser(email,password, callback){

            setTimeout(()=>{
            
                callback ({userEmail:email});
                
            }, 5000)
        }
        
        
        
function getUserContacts(email,callback){

            setTimeout(()=>{
            
                callback( ["contact1","contact2","contact3"] );
                
            },2000)
        }

So to display the user’s contacts we have to display his email first.

We have seen previously how to display the user’s email. All we have to do now is to add the function of the callback function of getUserConatcts() when invoked. So the code becomes:

JavaScript
function loginUser(email,password, callback){

            setTimeout(()=>{
            
                callback ({userEmail:email});
                
            }, 5000)
        }
        
        
        
function getUserContacts(email,callback){

            setTimeout(()=>{
            
                callback( ["contact1","contact2","contact3"] );
                
            },2000)
        }
        
    
 var user = loginUser("john@gmail.com", 32654, (emaildisplayed)=>{
 
            console.log(emaildisplayed);

                      getUserContacts(emaildisplayed.userEmail,(displayContacts)=>{
              
                      console.log(displayContacts);

         
            });

        });
        
        
        // {userEmail: 'john@gmail.com'}
        // ['contact1', 'contact2', 'contact3']

Now let’s say that we want to have the user’s contacts phone number.

To have the user’s contacts phone number we have to have the user’s email first, and then the user’s contacts, and then we can look for the user’s contacts phone number.

So the user’s contacts phone number information is linked to the user’s email and the user’s contacts.

The code is:

JavaScript
function loginUser(email,password, callback){

            setTimeout(()=>{
            
                callback ({userEmail:email});
                
            },5000)
        }

        function getUserContacts(email,callback){
        
            setTimeout(()=>{
            
                callback(["contact1","contact2","contact3"]);
                
            },2000)
        }

        function getPhoneNumber(contacts,callback){
        
            setTimeout(()=>{
            
                callback(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
            }, 3000);
        }

And to display the user’s contacts phone number we have to add the callback function in the invoked function:

JavaScript
function loginUser(email,password, callback){

            setTimeout(()=>{
            
                callback ({userEmail:email});
                
            },5000)
        }

        function getUserContacts(email,callback){
        
            setTimeout(()=>{
            
                callback(["contact1","contact2","contact3"]);
                
            },2000)
        }

        function getPhoneNumber(contacts,callback){
        
            setTimeout(()=>{
            
                callback(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
            }, 3000);
        }
        
        
        


var user = loginUser("john@gmail.com", 32654, (emaildisplayed)=>{

            console.log(emaildisplayed);

            getUserContacts(emaildisplayed.userEmail,(displayContacts)=>{
            
            console.log(displayContacts);

                getPhoneNumber(displayContacts[1],(displayPhones)=>{
                
                console.log(displayPhones);
                
                    })
            });

        });
        
        
      // {userEmail: 'john@gmail.com'}
     // ['contact1', 'contact2', 'contact3']
    // ['phoneNumber1', 'phoneNumber2', 'phoneNumber3']

As you can see, by now the code starts already to get complicated, as we have nested functions and callbacks. Continuing this way will make the code difficult to read, understand and maintain.

So let’s see how to make it clearer with fewer lines of code.

3)- Code written using promises

Below we will see a JavaScript promise example. We will remove all the callback functions and replace them with promises.

For example, for the user’s email the code will be:

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
            
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
             },5000)
            })
            
        }

To display the user’s email we will add:

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
            
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
             },5000)
            })
            
        }



loginUser("john@gmail.com", 32654).then(emaildisplayed => console.log(emaildisplayed));


// {userEmail: 'john@gmail.com'}

Here the callback function that was defined in the callbacks section is equivalent to the function mentioned in the then() syntax.

So if there is no error, the .then() method will be executed to log the results to the console.

Let’s add the rest of the code and display the user’s contacts and phone numbers.

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
                
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
              },5000)
            })
            
        }

        function getUserContacts(email){

            return new Promise((resolve,reject)=>{
               
                setTimeout(()=>{
               
                resolve(["contact1","contact2","contact3"]);
             
              },2000)
            })
            
        }

        function getPhoneNumber(contacts){

            return new Promise((resolve,reject)=>{
            
                setTimeout(()=>{
                
                resolve(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
              }, 3000);
            })
            
        }
        
        
        loginUser("john@gmail.com", 32654)
        
        .then(emaildisplayed => {
        
            console.log(emaildisplayed) ;
            
            return getUserContacts(emaildisplayed.userEmail)
        })
        
        .then(displayContacts => {
        
            console.log(displayContacts) ;
            
            return getPhoneNumber(displayContacts[1])
            
        })
        
        .then(displayPhones => console.log(displayPhones));

So here, just to keep it clear, the console.log(emaildisplayed) and console.log(displayContacts) and console.log(displayPhones) were kept to show that the email is displayed first and then the contacts and then the phone numbers. But in reality the code should be :

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
                
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
              },5000)
            })
            
        }

        function getUserContacts(email){

            return new Promise((resolve,reject)=>{
               
                setTimeout(()=>{
               
                resolve(["contact1","contact2","contact3"]);
             
              },2000)
            })
            
        }

        function getPhoneNumber(contacts){

            return new Promise((resolve,reject)=>{
            
                setTimeout(()=>{
                
                resolve(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
              }, 3000);
            })
            
        }
        
        
        loginUser("john@gmail.com", 32654)
        
        .then(emaildisplayed => getUserContacts(emaildisplayed.userEmail))
        
        .then(displayContacts => getPhoneNumber(displayContacts[1]))
        
        .then(displayPhones => console.log(displayPhones));
        
        // ['phoneNumber1', 'phoneNumber2', 'phoneNumber3']

Using this code we’ll have directly the final result displayed which is: [‘phoneNumber1’, ‘phoneNumber2’, ‘phoneNumber3’]. So each .then() method will return a new promise that will resolve and at the end we will have the phone numbers.

But what if I want to get back the results simultaneously. Meaning what if I don’t want to wait 5 seconds or 2 seconds and I want to get for example the results of loginUser(), getUserContacts() and getPhoneNumber() all of them at the same time?

Well, to do that we have to use the Promise.all() syntax.

4)- Promise.all()

The Promise.all() method is a powerful feature in JavaScript that allows you to work with multiple promises simultaneously. It takes an array of promises as its input and returns a new promise. This new promise will be fulfilled when all the input promises have been fulfilled, or it will be rejected if any of the input promises are rejected.

In short, Promise.all() method allows us to get back the data of all promises at the same time without having to wait for 5 seconds for a promise to be fulfilled first and then 2 seconds for another promise to be fulfilled…

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
                
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
              },5000)
            })
            
        }

        function getUserContacts(email){

            return new Promise((resolve,reject)=>{
               
                setTimeout(()=>{
               
                resolve(["contact1","contact2","contact3"]);
             
              },2000)
            })
            
        }

        function getPhoneNumber(contacts){

            return new Promise((resolve,reject)=>{
            
                setTimeout(()=>{
                
                resolve(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
              }, 3000);
            })
            
        }




Promise.all(  [loginUser("john@gmail.com", 32654), getUserContacts("john@gmail.com"),

getPhoneNumber(["contact1","contact2","contact3"])]  )

        .then(([user, contacts, phoneNumbers]) => {
        
                console.log("Logged in as:", user.userEmail);
                
                console.log("User contacts:", contacts);
                
                console.log("Phone numbers:", phoneNumbers);
                
            })
            
        .catch((error) => {
        
                console.log("Error:", error);
  });

In this code, the Promise.all() method is used to wait for the resolution of multiple promises. It takes an array of promises as an argument. In this case, we pass an array containing the promises returned by loginUser(), getUserContacts(), and getPhoneNumber().

The .then() method is used after Promise.all() to handle the resolved values of all the promises simultaneously. Since Promise.all() returns an array of resolved values in the same order as the original promises, we can destructure the resolved values in the .then() method and access them individually.

Inside the .then() method, you can access the resolved user, contacts, and phoneNumbers values and perform the desired actions with them.

By using Promise.all(), all the promises will execute concurrently, and the .then() method will be called only when all promises have resolved successfully. If any of the promises reject, the .catch() callback will be triggered.

5)- Async/ Await

What about if we want to write the code in a synchronous way? In a way like we are used to, avoiding all these .then() and .catch() methods.

All we have to do is to use the async/await syntax.

In the code below we will try to display the results of loginUser() function in a synchronous way.

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
                
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
              },5000)
            })
            
        }

        function getUserContacts(email){

            return new Promise((resolve,reject)=>{
               
                setTimeout(()=>{
               
                resolve(["contact1","contact2","contact3"]);
             
              },2000)
            })
            
        }

        function getPhoneNumber(contacts){

            return new Promise((resolve,reject)=>{
            
                setTimeout(()=>{
                
                resolve(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
              }, 3000);
            })
            
        }
        
        async function displayData(){

            var emailUser=await loginUser("john@gmail.com", 32654);
            
            console.log(emailUser);
            
            }


displayData();

To display the results in a synchronous way, which means instead for JavaScript to execute the next instructions waiting for the previous instructions to be fulfilled it will execute the instructions line by line, just like it is used to do, we created a function called displayData().

Inside this function we created a variable emailUser and assigned it the value of loginUser() just like we do in normal coding.

The only difference is that in front of displayData() we added the keyword async to tell Javascript that this is normally an asynchronous operation (the code we are trying to run inside the function) and we added the keyword await in front of loginUser() to tell JavaScript to wait for the execution of the promise.

And then we call the function displayData() as we normally do.

So to display the rest of the results of the other functions we will create variables and for each variable we will assign it the value of the function:

JavaScript
function loginUser(email,password){

            return new Promise((resolve, reject)=>{
                
                setTimeout(()=>{
                
                resolve ({userEmail:email});
                
              },5000)
            })
            
        }

        function getUserContacts(email){

            return new Promise((resolve,reject)=>{
               
                setTimeout(()=>{
               
                resolve(["contact1","contact2","contact3"]);
             
              },2000)
            })
            
        }

        function getPhoneNumber(contacts){

            return new Promise((resolve,reject)=>{
            
                setTimeout(()=>{
                
                resolve(["phoneNumber1","phoneNumber2","phoneNumber3"]);
                
              }, 3000);
            })
            
        }
        
        async function displayData(){

            var emailUser=await loginUser("john@gmail.com", 32654);
            
            var contacts=await getUserContacts(emailUser.userEmail);
            
            var phoneNumber=await getPhoneNumber(contacts[1]);
            
            console.log(emailUser);
            
            console.log(contacts);
            
            console.log(phoneNumber);
        }

        displayData();


By using async and await, you can write asynchronous code that resembles synchronous code, making it easier to understand and maintain. It eliminates the need for explicit promise chaining and nested callbacks, resulting in more concise and readable code.

Read More

How to Use Callback Functions in JavaScript: A Complete Guide with Examples and Best Practices

Object constructor functions: How does the Javascript constructor work?

Send form data received in the backend to the database (MongoDB)

Send form data to the back-end (Node JS)

Get the data back from the database (MongoDB) and display it on the front-end

How to send data from back-end(Node JS) to MongoDB database?

Inline vs Inline-Block vs Block CSS properties

absolute and relative positions: What’s the difference?

List style type CSS

Tables in HTML

Audio and Video HTML Tutorial – Learn how to add sound effects, videos and Youtube videos to your website

Leave a Reply

%d bloggers like this: