Javascript

Let’s understand Asynchronous programming in Javascript (async/await)

Introduction In a web application, there can be many events occurring simultaneously. For example, one section of the page requires data from...

Written by Luci · 4 min read >

Introduction

In a web application, there can be many events occurring simultaneously. For example, one section of the page requires data from the server to print the layout. Other sections may only require the html, css and javascript to render the layout on the page.

Therefore, it becomes difficult to maintain the performance and responsiveness of the UI if we rely only on synchronous programming which runs the script in the given order and does not listen to the next instruction until the current line of code finishes its execution.

Asynchronous programming in javascript was introduced to solve this problem. It takes the code out of the script and executes it elsewhere without blocking the main thread. Eventually, the browser will be presented with the result when the server has sent the request.

What is synchronous programming?

After running a javascript file the interpreter creates a container which is known as execution context. Everything in javascript happens within this context.

Execution context is comprised of two parts:

  1. The first one is the variable environment which stores the value of all the declared variables and functions in a key/value pair format.

2. The second one is the thread of execution which runs the script one line at a time in the exact same order it is written in the script. The thread will not move to the next line of instruction until it executes the current line of code completely. This makes javascript a “single-threaded synchronous language”.

var a = 10 ;

function multiply() {
  for(var i=1; i<=20; i++){
    a*= i;
  }
  return a;
}

b = multiply();

console.log(b)

In the above code, the script will not log the value of b until the multiply function executes and return a value. Therefore, the thread will wait until the multiply function runs and then move to the next line and print the value of b in the console. This is a simple explanation of how synchronous programming works. One line at a time.

Now, think of a situation where you want to get some data from an external server. Response from that server may depend on various factors such as the number of requests it receives from different sources at a time, the speed of the internet, the amount of data it has to fetch from the database etc.

All these tasks will take a specific amount of time to process the request and return a response to the client-side server. This means the thread will wait for the requested server to return a response and will stop the rest of the operations.

But getting data from another server is an external process which does not require the client-side processor for execution and the processor can perform other operations instead of waiting idly for the external server request. Asynchronous programming solves this issue and lets you write code in a different order.

How does asynchronous programming work?

In an asynchronous program, we can perform multiple actions simultaneously such as getting data from two different external sources and combining them together.

The program identifies the line of code which is asynchronous and takes it apart from the main execution. When the server finishes sending the request the program is notified and presented with the result.

To know how all these happen we have to understand the concept of an event loop and how it runs multiple actions at the same time.

What is an Event loop?

The javascript engine creates a global execution context and a call stack after running the script.

The call stack is a data structure used by JavaScript to manage function calls. It keeps track of the order in which functions are called and the state of each function. Whenever a function is called, its information is pushed onto the top of the call stack. When the function returns, its information is popped off the stack.

After the global execution context creation, every instruction which returns some value is again put into a local execution context.

The important thing to understand here is that the scope of an execution context depends upon its parent container (parent execution context in which it is created). A parent container will not leave the stack until all its child contexts finish their execution.

Let’s understand it with an example:

function A() {
  console.log('Function A');
  B();
}

function B() {
  console.log('Function B');
  C();
}

function C() {
  console.log('Function C');
}
A();

In the above code, three functions are declared but they are called in different scopes.

When we call the function named A it is pushed into the stack.

Function A consoles some output and then calls function B. In this step function, B is added to the stack but A is not popped out of the stack yet because A has not returned its value yet.

Now when function B is called it also consoles an output and calls function C which is then added to the stack.

Next, C returns a value and popped out of the stack and then B also finishes its execution and is popped out of the stack.

Finally, A will finish its execution and be popped out of the stack leaving the stack empty.

The order of their position in the call stack will be like this:

Identifying asynchronous calls

JavaScript identifies asynchronous calls through the use of callback functions, promises, and async/await syntax.

When a function is called asynchronously, it means that the function is not executed immediately, but rather it is scheduled to run at a later time. This is typically used when performing time-consuming operations that would otherwise block the main thread and make the application unresponsive.

Callbacks are functions that are passed as arguments to other functions and are executed when a particular event occurs, such as when an asynchronous operation completes. For example, the setTimeout function is an asynchronous function that takes a callback function as its first argument and schedules the function to be executed after a certain amount of time has passed.

Promises are another way to handle asynchronous calls in JavaScript. A promise is an object that represents the eventual completion or failure of an asynchronous operation and allows you to attach callbacks to be executed when the operation completes or fails.

The async/await syntax is a newer way to handle asynchronous calls in JavaScript that were introduced in ES2017. It provides a more concise and readable way to write asynchronous code by allowing you to write asynchronous code that looks like synchronous code.

How javascript uses event loop to execute asynchronous calls?

The event loop is a key mechanism in JavaScript for managing asynchronous code execution. It continuously checks the call stack and the message queue for tasks that need to be executed. When the call stack is empty, it pulls the next task from the message queue and pushes it onto the call stack for execution.

This allows for the efficient handling of asynchronous operations without blocking the main thread. The event loop is a fundamental concept in modern web development and is crucial for building responsive and performant applications.

  1. Define an async function using the async keyword. This function can contain one or more await expressions.
async function fetchData() {
  const response = await fetch('https://example.com/data');
  const data = await response.json();
  return data;
}

2. Inside the async function, use the await keyword to wait for a Promise to resolve before continuing with the rest of the code. In the example above, the await keyword is used twice to wait for the fetch and json methods to return a value.

3. Call the async function using the await keyword to wait for it to complete before moving on to the next line of code.

async function main() {
  const data = await fetchData();
  console.log(data);
}

In the example above, the main function calls the fetchData function and waits for it to complete before logging the returned data to the console.

Conclusion

Using async/await syntax is a powerful tool for handling asynchronous code in JavaScript, but it’s important to remember that it’s not a replacement for Promises or callbacks, but rather a way to make working with them more readable and easier to understand.

Written by Luci
I am a multidisciplinary designer and developer with a main focus on Digital Design and Branding, located in Cluj Napoca, Romania. Profile