How does async work in JavaScript? -

How does async work in JavaScript?

It’s been a while since I wrote a JavaScript article, so here it is! Today we are going to reveal one of the biggest front-end mystery. How does async work in JavaScript? The question might seem quite obvious, but believe me that many developers think that it’s because of multi-threading. Well, here is the thing. Async does not require more than one thread. If you don’t believe me click here to confirm that. Ok, fine, let’s say that we need multi-threading to do async. But here comes the thing that confuses people at that point. JavaScript runtime is single-threaded. And that’s where things start to become more complicated.


Synchronous call

Before we start discussing the async, it’s worthwhile to explain what is the synchronous call. So, let’s do that using the example of McDonald’s and more precisely McDrive. I guess that everybody knows how does it work. To get some food first, we need to ride up to the position when we speak to staff and order what we want. Next, we pay for our order in the other post. Then we ride up to the last post, and we wait for our food patiently. The problem is that if someone makes a big order, it will take a long time to make food. McDonald’s can not say “please leave and get back to us in 10 minutes.” The client has to wait until it’s done. So even if you only ordered one cheeseburger, you have to wait until the guy in front of you will leave. That is not efficient. Let’s see the representation of that situation in JavaScript:


function handleSync(){

    let orderTime = Math.floor(Math.random() * 1000)

    for(let = 0; i< orderTime; ++i) {}

    console.log("complex order")


console.log("coca cola")


console.log("one cheeseburger")


Now let’s say that we called that function by clicking a button. We click it and… UI freezes. That’s because our thread is waiting for the completion of the handleSync function. Let’s see how the call stack looks like:




It’s such a waste of time because in the meantime we could print “One cheeseburger” message, and more importantly, we could unfreeze UI for the user. And that’s when async comes into play.


Asynchronous call

To explain what is the asynchronous call we’ll use another McDonald’s analogy. For several years, the Polish system of ordering a meal in a restaurant looks a little different. After paying for the order, you get a quickie, which is triggered when the food is ready for you. However, there is little detail. Having a number less than the other person does not necessarily mean that we get food before.  Everything depends on the complexity of our order. So, McDonald’s doesn’t have to stop sales as  in the McDrive. They can carry out other tasks, and when cooks inform cashiers about the readiness of the order, they stop selling immediately and call us to take the food out. Let’s see the representation of that situation in JavaScript:


function handleAsync(){

         console.log("complex order")


console.log("coca cola")




Now, the main difference lies in the fact that after calling handleAsync function, our thread will not be waiting until it ends. Instead, the console will first display the “one cheeseburger”, and after 5 seconds will show “complex order”. Since our thread will not be stopped, it also means that the UI won’t freeze. So now, let’s take a look at the call stack:




Wait! What the hell happened out there?! Things disappeared and reappeared on the stack? How is it possible? Well, it is time to reveal the mystery.


WebAPIs, task queue and event loop

The problem with the above diagram is that it is not complete. To understand how the browser handles asynchronous invocations, we need to realize that apart from JavaScript runtime it uses several “mechanisms.” The diagram below shows that:


Generally speaking, the browser provides us some APIs, which operates outside the JS runtime, so that we can treat them as additional threads (more or less). For instance, the JavaScript API can handle the setTimeout for you, so we can safely remove that from the call stack. Thanks to that, the user thinks that the async operation runs concurrently because several things work at the same time. Here is the full list of APIs for Google Chrome. Now, let’s say that our setTimeout countdown completes. What we could do is to put it back onto the call stack. But that would be unpredictable; we can’t just do it randomly. So instead, the API pushes it to the task queue. And now is the time where the event loop comes into play. Its job is simple. It looks on the stack, and if it’s empty, it takes the first thing that’s in the task queue and pushes it onto the stack. That’s it. And that’s how previously the things magically reappeared. Ok let’s take a look at the whole process:




So, I hope that it’s clear for you now. But can we prove that it works that way? Yes, we can do it pretty easily:






What will appear on the console? It seems logical that if setTimeout takes 0 seconds, it’ll execute immediately. Therefore, the answer will be “1”, “2”, “3”. But it’s incorrect. Even if setTimeout takes 0 seconds, it will be first placed in WebAPI and immediately after that on the task queue. Then, event loop will check whether the call stack is empty, but at that time console.log(“3”) will be executing. So, it will keep our callback in a queue until the stack is empty. That’s why the correct answer is: “1”, “3”, “2”.



I hope that today’s article was useful for some of you. If you want to play with that concept more, you can check excellent runtime visualization called loupe created by Philip Roberts. Next week we’ll return to the subject CQRS and Event Sourcing. So, if you don’t want to miss that, follow me on Twitter or Facebook .

You may also like...