How Async JS actually work

Simplifying the execution of Asynchronous operations in JavaScript as well as comparing with Synchronous counterparts.

Difference between async and sync functions

Before starting with the nature of synchronous JavaScript functions, let's look at how JavaScript working on a single core plays a crucial role in executing the above functions. JS being an interpreted language executes line-by-line thus using a single core, well this is how synchronous functions work too.

function convertNumToString(number){
    return number.toString()
}
let result = convertNumToString(5);
console.log("Successfully converted from Number to String");

The next line of code is not executed until the current is executed, this is how most functions operate. In the above lines of code, after the "result" variable is calculated the message is printed in the console, and so forth. The above function "convertNumToString" is synchronous as it obeys the above definition.

Asynchronous Functions - These functions perform an async task such as executing an HTTP call, using a setTimeout function, or using a file-system library in JS. Making the JS core wait for the above tasks will inherently decrease the efficiency and that is the reason for the asynchronous function the core does not wait until it is executed completely.

function getDataFromServer() {
  fetch("get-api-data/getData").then((resp) => {
    resp.json().then((data) => {
      return data;
    });
  });
}
let result = getDataFromServer();
console.log("Processing data from the server");

Rather than the JS core waiting for the "result" variable in the above lines of code, the console is printed first, and then the async function "getDataFromServer" is executed. These functions are handled using "Promises" where in the "then" function we get the results after its execution.

Behind the scenes of Async Functions

Let's simplify it further by visualizing how async functions are executed under the hood, suppose the core sees an async function rather than executing it by itself, it delegates the function and goes on continuing its execution in the Call Stack - a place where all the current JS operations are executed.

Let's take one more example of how a global async function in JS "setTimeout" is executed. The above function takes in two parameters, one is the function to be executed provided as a callback and the other parameter is the time (in milliseconds) by the which callback function is to be delayed.

function sayHi(){
    console.log("Hi");
}
setTimeout(sayHi, 1000);
console.log("Hello");

Here the setTimeout being an async function is delegated by the JS core and hence "Hello" is printed and proceeded by "Hi" with a duration between them almost equal to 1000ms. The delegated functions are handled by Web Apis as visualized in the above image, these functions after execution are stored in the Callback Queue and pulled in the Call Stack only after the current operations in the stack are completed by Event Loop. The Call Stack is the place where the core resides and executes all operations until the stack is cleared.

All these executions are visualized in real-time at http://latentflip.com/loupe/

Connect with me - https://linktr.ee/kamran_khan_

Did you find this article valuable?

Support Kamran Khan's blog by becoming a sponsor. Any amount is appreciated!