Rajat Gupta
Rajat's Blog

Rajat's Blog

promises and async-await in JavaScript

promises and async-await in JavaScript

Rajat Gupta's photo
Rajat Gupta
·Mar 16, 2022·

7 min read

Subscribe to my newsletter and never miss my upcoming articles

Let's see what MDN has to say:

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

A Promise can be in one of the following states:

  1. pending: initial state, neither fulfilled nor rejected.

  2. fulfilled: meaning that the operation was completed successfully.

  3. rejected: meaning that the operation failed.

JavaScript is a synchronous and single-threaded language. It basically means that it does one task at a time. The javascript code runs from top to bottom and if there is a code block that is doing some complex calculations then all the code below that block will not run until the code block above has finished executing. To learn more about this, read my blog here: rajatgupta.net/javascript-single-threaded-a...

We use callbacks with setTimeout to make JavaScript behave Asynchronously. Here's an example of how setTimeout makes JS async.

setTimeout(()=>console.log("Welcome to my blog, Elon"), 5000)
console.log(2 + 2);

Result:

4
Welcome to my blog, Elon

As you can see above that although the welcome statement is written first, it is printed after the second statement (2+2 = 4). Hence, we just made the code asynchronous.

Now, the problem with using callbacks is the Callback hell.

getA(getB(getC))

getA(){
    doX();
    doY()
    getB(data => {
            doOne();
            doTwo();
            getC(cData => {
                    doEleven()
                    doTwelve();
                }
            }
}

We call it ☝️ callback hell because the code is not easy to follow and soon becomes messy (after adding a few more functions).

Here Promise comes into the picture.

Let's understand promises: In real life, the promise is mostly used when we need to get some data or response from the network. promise in JS is the same as promise in real life.

I promise you that you'll understand promises after reading this blog. Now 3 things can happen:

  1. promise is resolved: You understood promises in JS.
  2. promise is rejected: I wasted your time, you still did not understand promises.
  3. Promise is pending: you are still reading.

Syntax of promise:

callAPromise().then(successHandler).catch(rejectHandler)

First, we call a promise. If the promise is resolved then whatever is inside .then will run. However, if the promise is rejected then whatever is inside .catch will run. Yaa! It's that simple.

promises are really great when we wanna do something in the background for example downloading an image from a different server and meanwhile doing whatever we are doing instead of waiting for the image download to finish and if the image downloading fails we can catch it and give an error message to the user.

Now, let us do some question based on the below promise:

function fakeFetch(msg, shouldReject) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldReject) {
        reject(`error from server: ${msg}`)
      }
      resolve(`from server: ${msg}`)
    }, 3000)
  })
}

Note: You don't have to write your own promise at this stage, just understand it ☝️ and do the questions given below (in browser console) as you read.

Question 1: use the fakeFetch() to get data and show on success?

fakeFetch('I am awesome').then(response => console.log(response).catch(response => console.log("This won't run")))

Result:

Promise {<pending>}[[Prototype]]
from server: I am awesome

Here is what's happening:

1 .then and .catch are the methods of Promise.

  1. When we don't pass the 2nd parameter in the fakeFetch, the promise is resolved else it gets rejected.

  2. As soon as we call fakeFetch('I am awesome'), the I am awesome is passed to the msg parameter of fakeFetch. However, nothing will be passed to the shouldReject parameter of fakeFectch.

  3. The fakeFetch will return a promise after 3 seconds as we have set the delay of 3 seconds. Hence, for the initial 3 seconds, the promise will be in the pending state.

  4. but what do I mean when I say a promise will be returned: I mean that since there is no shouldReject, the promise will be resolved, and from server: ${msg} will be passed as a parameter (response) inside the .then method and then we can do whatever we want with this parameter (response). Here I just printed it in the console.

Question 2: Call fakeFetch(msg, true) to get a rejected promise. Handle the error with the error handler. Show a message using console.error for errors?

fakeFetch('I am awesome', 'anything').then(response => console.log(response).catch(response => console.error(response))

Result: 1.PNG

In the 2nd question ☝️, the promise will be rejected as we have passed the value to the shouldReject parameter and hence the catch part will run. As far as console.error is concerned, I used it instead of console.log just to show the error in red.

Question 3: Create a function getServerResponseLength(msg) This function will use fakeFetch() internally with the message and return the length of the response received by the server?

function getServerResponseLength(msg){
    fakeFetch(msg).then(response => console.log(response.length))
}

getServerResponseLength('I am awesome');


Result: 25

As I told you before that we can do anything with the response we get from the server and here instead of printing the response, we calculated its length.

Question 4: Write a function syncCallsToServer(msg1, msg2) which will take two messages and call fakeFetch() with the second message only when the first message has returned from the server.

function syncCallsToServer(msg1, msg2){
    fakeFetch(msg1).then(response1 => fakeFetch(msg2).then(response2 => console.log({response1, response2})))
}

syncCallsToServer('I am awesome', 'react is also awesome');



Result:
{response1: 'from server: I am awesome', response2: 'from server: react is also awesome'}

Just read the above code again and you'll understand what's happening. In case you don't read this => This is nesting. In the syncCallsToServer function we passed 2 parameters, msg1 and msg2. However in the fakeFetch we only passed msg1 and since there is no 2nd argument to pass inside shouldReject, the promise will be resolved then we'll pass the msg2 in fakeFetch and then finally we'll print both the responses.

In the above code, it will take 6 seconds to get the result (3 seconds for each fakeFetch() call). However we can also do the same thing in parallel and it will take only 3 seconds to get both the results printed. See below.

function syncCallsToServer(msg1, msg2){
    fakeFetch(msg1).then(response1 => console.log({response1})
    fakeFetch(msg2).then(response2 => console.log({response2})
}

syncCallsToServer('I am awesome', 'react is also awesome');



Result:
{response1: 'from server: I am awesome'} 
{response2: 'from server: react is also awesome'}

The above responses will take only 3 seconds (parallel call)

Async-Await:

Although this is just syntactic sugar and nothing else, I recommend using this.
Let's see the syntax in terms of arrow function:

// Doing this in es6 arrow function would be

const printDataFromServer = async () => {
    try {
        const serverData = await anyPromiseWhichWillReturnData();
      console.log(serverData);
    } catch (err) {
     console.error(err)
    }
}

In arrow function async keyword is used before the (). While in normal functions, it is used before the function keyword itself. Let's see the async-await syntax with normal function.

async function printDataFromServer() {
  const serverData = await anyPromiseWhichWillReturnData()
  console.log(serverData);
}

Note: Always take care of error handling.

Now, let's do some questions.

Question 5: Call fakeFetch() with some msg and use await to get the data and then print it.

const testing = async (msg) => {
    try{
        const serverData = await fakeFetch(msg);
        console.log(serverData);
    }
    catch (err){
        console.log(err)
    }
}
testing('I am awesome')
Promise {<pending>}
from server: I am awesome

In the above code, await is saying that until the promise (fakeFetch) is returned don't execute the next line. rest I think, you can understand.

Question 6: Write a function syncCallsToServer(msg1, msg2) which will take two messages and call fakeFetch() with the second message only when the first message has returned from the server. use async-await for this purpose.

    const testing = async (msg1, msg2) => {
        try{
            const serverDataOne = await fakeFetch(msg1);
            const serverDataTwo = await fakeFetch(msg2);   
            console.log({serverDataOne, serverDataTwo})  
        }
        catch (err){
            console.log(err)
        }

    }
testing('I am awesome', 'react is also awesome');

Promise {<pending>}
{serverDataOne: 'from server: I am awesome', serverDataTwo: 'from server: react is also awesome'}

Although we can also do the above question without using try-catch. However, I recommend you to always use try-catch.

If you wanna read more about async-await, read it here: javascript.info/async-await.

If you have any doubt ask me in the comments section and I'll try to answer as soon as possible.

I write 3 articles related to web development every single week. Subscribe to my newsletter (It's free) here[getrevue.co/profile/therajatg], if you are learning the same.

Twitter: @therajatg

PS: show some love by giving a thumbs up.

Have an awesome day ahead 😀!

 
Share this