DEV Community

Cover image for What does it even mean to be Asynchronous in JavaScript?
IanMcbull
IanMcbull

Posted on • Edited on

What does it even mean to be Asynchronous in JavaScript?

A meme of a confused individual

All around us, we have synchronous and asynchronous activities happening. Can you think of any examples of synchronous activities that you perform on a day-to-day basis?
· Taking a shower
· Sleeping

What about asynchronous activities?
· Cooking
· Watching TV

We've all heard the phrase, 'you cannot serve two masters at a time. This is exactly what synchronous means. You have to do one thing at a time. More often than not, the nature of the operation will not allow you to do anything else.

An easy-to-understand activity that we all do, is taking a shower. Have you ever tried to take a shower and cook at the same time? Don't try it, it won't end well. Have you ever found yourself taking a jog while you're asleep? Probably not.

These are day-to-day examples of synchronous activities that we all perform.
What about asynchronous operations? Watching TV is an asynchronous activity. You can watch TV and eat pizza. You can watch TV and sleep. Cooking is also an asynchronous activity. Just because the rice isn't ready yet, doesn't mean you can't make a phone call, or do dishes. You can do something else as the rice is cooking. This is what we mean by asynchronous tasks. You can do other things while the async task is being fulfilled.

Now let's look at why we need asynchronous code. Couldn't we just write code synchronously and be on our merry way. Well, yes, we can. Let's look at an example.

console.log("Step 1");

for(let i = 0; i <= 9; i++){
  console.log(i)
}

console.log("Step 2")
Enter fullscreen mode Exit fullscreen mode

JavaScript code runs from top to bottom. The code is executed line by line, in order.

So in our example the first line will be executed and will print "Step 1" to the console. Then, the for loop will start executing and print numbers 0 through 9. When this operation is complete, the next line will begin to execute and print "Step 2" to the console.

As you can see, the for loop had to be executed in its entirety first, before the next line of code could run. Now look at the same example, but this time, we'll increase the range of numbers.

console.log("Step one")

for(let i = 0; i < 1000000;i++){
console.log(i)
}

console.log("Step 2")
Enter fullscreen mode Exit fullscreen mode

Chances are you'll be waiting for a while. As the loop runs, your browser tab will become unresponsive until the loop is done executing. The main JavaScript process is blocked by the synchronous loop. Synchronous code is also referred to as blocking code.

A meme of a person shouting
Your browser will throw a tantrum when a long running process blocks the main thread.

You can see how this can become problematic. So how do we solve this? Cue in asynchronous code, which is also referred to as non-blocking code.

JavaScript provides three main ways of writing non-blocking code.

  • Callbacks
  • Promises
  • Async/Await

An image of spongebob making a call

Callbacks

A callback is a function that gets passed to another function as a parameter. Let's look at an example of a built-in JavaScript function, that takes in another function as a parameter.

setTimeout(()=>{

console.log("Some asynchronous action")

},1000)
Enter fullscreen mode Exit fullscreen mode

A setTimeout function will wait for a given number of milliseconds, then call its callback function. Callbacks were the de-facto way of handling asynchronous operations in JavaScript for a long time.

Then came ES6, which introduced Promises. We'll look at promises in the next section. The problem with callbacks is as your asynchronous operations increase so does the number of callbacks. This is referred to as callback hell. Let's look at an example.

const stepOne = () => {
  console.log("Step 1");
};
const stepTwo = () => {
  console.log("Step 2");
};
const stepThree = () => {
  console.log("Step 3");
};
const stepFour = () => {
  console.log("Step 4");
};

setTimeout(() => {
  stepOne();
  setTimeout(() => {
    stepTwo();
    setTimeout(() => {
      stepThree();
      setTimeout(() => {
        stepFour();
      }, 2000);
    }, 3000);
  }, 2000);
}, 1000);
Enter fullscreen mode Exit fullscreen mode

As your asynchronous operations increase, your code becomes harder to read.

Two hands making a promise
Promises were introduced in ES6 as a modern solution for handling asynchronous code in JavaScript

Promises

Promises were introduced in ES6(ECMAScript 2015) as a modern solution to handling asynchronous operations. Promises consist of 3 states:

  • Pending
  • Fulfilled
  • Rejected

When a promise is pending, it has neither been fulfilled nor rejected. As the name suggests, the operation is still pending. 
Once the operation is complete, the promise will return one of two states. It will either resolve or reject. If the promise resolves, it means it ran successfully and we can access the value of the promise. If an error occurred, the promise will be rejected with an error. Let's look at an example.

const marvelOrDC = (answer) =>{
    // Let's start by capitalizing the user input 
    answer = answer.toUpperCase()
    // This is how we return a promise
    return new Promise((resolve,reject)=>{ 
    if(answer === 'MARVEL'){
     resolve("You are a Marvel comics fan")
    }else if(answer === "DC"){
    resolve("You are a DC comics fan")
    }
      // If the user inputs anything other than Marvel or DC the promise will be rejected
      else{
      reject("You need to either pick marvel or DC")
      }
    })
  }

  marvelOrDC("")
Enter fullscreen mode Exit fullscreen mode

So how do we access the value that is returned by the promise? We call the .then() method on our promise.

marvelOrDC("").then(data=>{
      console.log(data)
  },(err)=>console.log(err))
Enter fullscreen mode Exit fullscreen mode

The .then() method takes in two parameters. The first is a callback function that gets called when the promise resolves. The second is a callback function that gets called if the promise is rejected.
But there is a cleaner way of handling rejections, using the .catch() method.

marvelOrDC("")
  .then(data=>{
      console.log(data)
  })
  .catch(err=>console.log(err))
Enter fullscreen mode Exit fullscreen mode

Now that we have a sense of how to write promises, we can begin to look at async/await, which use promises under the hood.

An image of an hour glass
Async/Await syntax was introduced in ES2017

Async/Await

The async/await syntax is syntactic sugar for writing promises. It's a cleaner less verbose way of handling promises. When handling promises using, async/await, we don't need to chain the .then() or the .catch() methods. Let's rewrite the previous example using async/await to see what I mean.

const marvelOrDC = (answer) =>{
    // Let's start by capitalizing the user input 
    answer = answer.toUpperCase()
    // This is how we return a promise
    return new Promise((resolve,reject)=>{ 
    if(answer === 'MARVEL'){
     resolve("You are a Marvel comics fan")
    }else if(answer === "DC"){
    resolve("You are a DC comics fan")
    }
      // If the user inputs anything other than Marvel or DC the promise will be rejected
      else{
      reject("You need to either pick marvel or DC")
      }
    })
  }


const handlePromise = async(answer) =>{
  try {
    const result = await marvelOrDC(answer)
    console.log(result)
  } catch (error) {
      console.log(error)
  }
}
handlePromise("")
Enter fullscreen mode Exit fullscreen mode

Our handlePromise function is labeled with the keyword async. This allows the function to handle promises as well as facilitate the use of the await keyword, inside the function. 

The try/catch block is used to handle the different responses the promise returns. The await keyword will suspend execution until the promise is either resolved, or rejected. If resolved, then result will be logged. If rejected, then the error is logged instead.

An image of a person sweating

Conclusion

We've managed to cover a lot of ground. So kudos to you. This can hopefully serve as a stepping stone for you in your journey to master JavaScript.

Top comments (0)