​Asynchronous programming with JavaScript

JavaScript is a single-threaded language (single call stack). So, how can it be asynchronous? Are you also having the same thoughts? This article will explain how we can do asynchronous programming in JavaScript.

However, before getting into that, let us first understand what asynchronous programming is and the difference between synchronous and asynchronous coding.

What is Asynchronous Programming

Picture depicting the concept of asynchronous programming by an example of kitchen.

At its core, asynchronous programming is about executing tasks outside of the main program flow. Therefore, the program will continue running without waiting to complete the outside tasks. Once the asynchronous tasks finish their execution, the results are usually handled using callbacks, promises, or async/await, especially in JavaScript.

Why Use Asynchronous Programming?

  1. Improved Performance: By not waiting to complete the simultaneous tasks before moving on, applications can execute other tasks, using available resources fully and often completing multiple tasks in near parallel.
  2. Responsive Interfaces: Especially in web development, asynchronous code ensures that user interfaces remain interactive and responsive, even when handling heavy tasks in the background.
  3. Efficient Resource Utilization: Instead of stalling the main thread, asynchronicity often allows for better use of system resources, as the main program can continue processing other tasks.
  4. Scalability: Asynchronous systems, especially on the server side, can often handle more simultaneous connections or users since each connection isn’t tying up resources while waiting for I/O operations.
  5. Flexible Task Management: Asynchronous programming often comes with mechanisms to handle tasks, like queuing them up, prioritizing them, or even canceling them if there is no need for them.

Asynchronous Vs. Synchronous Programming

In summary, while synchronous programming provides linear flow and predictability, asynchronous programming offers performance, responsiveness, and resource utilization advantages, especially pertinent in scenarios with the potential for high I/O operations or user interactions.

The sweet and simple way to explain the difference is when we have two lines of code, say L1 and L2. Here, the execution of L2 code does not occur until the execution of L1. This is synchronous programming, and the opposite of the same is asynchronous programming. Let’s understand with the help of an example:

The output of the console:

L1: console.log('asynchronous programming'); 
L2: console.log('JavaScript');

As we can see, the L1 line will consistently execute first, followed by the L2 line of code. Therefore, this is why we call it synchronous programming. Let us see another example:

L1: setTimeout(function() { console.log("asynchronous programming"); }, 0); 

L2: console.log('JavaScript');

The output of the console:

JavaScript

asynchronous programming

Now, the second line output is printed first! This is because the “setTimeout” is an asynchronous function in JavaScript. This is what we will understand further and demystify how asynchronous code works.

Example of Why We Need Asynchronous Programming?

Let us see what will happen if JavaScript doesn’t have an asynchronous approach. Refer to the code shown below:

var foo = getasyn('/getFoo'); 

var bar = getasyn('/getBar'); 

console.log('JavaScript'); 

console.log(foo); 

console.log(bar);

Now, let’s forget about asynchronous programming and see how the above code behaves if it runs synchronously.

Firstly, “foo” will execute, blocking code execution further. Moreover, it will block the browser, and the user cannot perform any action) until the response is received. Next, the “bar” will execute, blocking the execution of code until the response is received. And then finally, the “console.log” statements will execute. 

Frustrating!!! The browser is blocked, and we can’t do anything because the call stack has executing statements. So, when the call stack is not cleared, we can’t do anything in the browser. That is the reason why we need an asynchronous approach in JavaScript.

Before diving in further, let me briefly introduce a V8 engine, as I will use this term in this post.

V8 Engine

The V8 JavaScript Engine is an open-source JavaScript engine from the Chromium Project for the Google Chrome web browser. V8 translates JavaScript code into more efficient machine code instead of an interpreter.

JavaScript Components for Asynchronous Programming

JavaScript can perform asynchronous programming with the help of Web APIs.
The components for doing asynchronous programming in JavaScript are as follows:

  1. Heap
  2. Stack
  3. WebAPIs
  4. Event loop
  5. Callback queue

There are a lot of JavaScript features that have asynchronous behavior like:

  1. setTimeout
  2. callback functions
  3. Fetch API and a lot more.

Let’s see this in detail and how asynchronous programming works. I will demonstrate this with the help of the “setTimeout” function.

Heap

Is it correct to say data in JavaScript is stored in the memory heap?

NO! Because JavaScript follows ECMAScript standards, and it doesn’t have any layout for memory storage. So, V8 (on Chrome browser) or another engine can store data.  And yes, in V8, variables are stored in heap memory space.

Stack or Call Stack

The JavaScript code about to execute will be pushed on the stack. Therefore, upon execution, it will pop from the stack. And if the function (f1) calls, another function (f2) will be pushed on top of f1. And once f2 is executed, it will pop out, followed by f1 when execution is completed.

OK! You might have heard of stack overflow and wondered what it is. Let me show you with the help of the following example:

function asyncProgram() {
 asyncProgram();
} 

asyncProgram();

It’s very clear from the diagram that as ‘asyncProgram()’ function calls itself, one point comes when there is no capacity in the stack, and stack overflow happens.

Web APIs

As we know, JavaScript can do only one task at a time, and all the code is pushed to the stack and pops out when executed.

WebAPIs help us to achieve asynchronous behavior. 

In web development, especially when discussing asynchronous operations in browsers, “Web API” refers to functionalities provided by the browser environment (outside of the JavaScript runtime) to handle tasks needed for asynchronous operations.

WebAPI Functionality for Asynchronous Operations

  • Timers: setTimeout(), setInterval()
  • AJAX Requests: XMLHttpRequest, Fetch API
  • DOM Manipulations: Methods to interact with the website’s Document Object Model.
  • Browser Events: User interactions like clicks, keyboard presses, or more complex events like IntersectionObserver.
  • Other operations: IndexedDB (for local storage), requestAnimationFrame (for animations), Geolocation API, etc.

These APIs are non-blocking and often asynchronous. Here’s how they fit into the larger picture:

When you run JavaScript code that invokes an asynchronous Web API (e.g., making a fetch request or setting a timer), the following happens:

  1. The Web API call is made, but JavaScript doesn’t wait for it to be completed. Instead, it continues running any subsequent code.
  2. Once the Web API operation is done (e.g., the timer elapses or the fetch request gets a response), it pushes a callback function to a task queue.
  3. The JavaScript runtime has an event loop that constantly checks if the main thread (where your synchronous JS code runs) is busy. When it’s not, the event loop takes tasks from the task queue and executes them.

This mechanism is why you can make a fetch request or set a timer and then handle the results or actions asynchronously, ensuring that these operations don’t block the main thread and keep the web page responsive.

As soon as the stack sees some asynchronous call on the stack, it sends that to Web APIs and then carries on with executing the remaining code. Once the Web API has resolved the async call, it somehow magically returns a response from an async call to the stack and gets executed.

Callback queue

Once Web API has resolved the response, it sends it to the callback queue because it cannot just simply send it to the call stack because it will interrupt the execution of the running code.

Event loop

The magic I talked about in the point above (WebAPIs) is the Event Loop. Its responsibility is to monitor the callback queue and call stack. When it finds that the call stack is empty and has something in the callback queue, it will queue requests from the callback queue to the call stack in a queue fashion (First come, first served).

Also, some of us have questioned why ‘setTimeout(function() {}, 0)’ doesn’t execute immediately. The reason is clear now: the call stack will push it to Web API and Web API to the callback queue. Now, the callback queue will wait till the time stack is not empty. The stack will get empty after executing all the JavaScript code.

There is a visual representation of how stack, event loop, callback, and queue interact with each other to understand the above explanation visually. Please follow the link: http://latentflip.com/loupe; it will demonstrate what happens behind the scenes to achieve asynchronous programming.​

Conclusion

Using the asynchronous nature of JavaScript is pivotal for developers aiming to create high-performance and user-friendly web applications.

Asynchronous programming in JavaScript allows efficient task management, optimizing responsiveness, and ensuring smooth user interactions.

By understanding and leveraging the distinctions between synchronous and asynchronous operations, JavaScript developers can master the art of handling multiple tasks concurrently, offering users a seamless digital experience.

As the web continues to evolve, so will the tools and techniques, but the principles of asynchronicity will remain a cornerstone of effective web development.

Tavish lives in Hyderabad, India, and works as a result-oriented data scientist specializing in improving the major key performance business indicators. 

He understands how data can be used for business excellence and is a focused learner who enjoys sharing knowledge.

Need help?

Let us know about your question or problem and we will reach out to you.