Asynchronous JavaScript: Everything you need to know

scofieldidehen

Scofield Idehen

Posted on July 24, 2023

Asynchronous JavaScript: Everything you need to know

This article was written by Adeola Ajiboso

Asynchronous simply refers to making many events occur simultaneously in any order without waiting for one another, and this definition applies to asynchronous Javascript.

​​
​​In this article, I'll go through the concept of asynchronous JavaScript, Callback, Promises, Async/Await, Callback queue, Event Loop, and Web browser features (Web API).
​​
​​Javascript is a single-threaded language that can only run one process at a time. JavaScript uses an event loop to carry out the operation, for now, consider the event loop as a queue where all Javascript processes are maintained and executed one at a time.

You should note Javascript is actually a Synchronous language. JavaScript executes lines of code sequentially, not concurrently, because it is by default, synchronous and single-threaded.

​​For instance, the code below will run and be executed line by line.

​​let username = "John";
​​let points = 5000;
​​let message = `${username} has earned ${points} points`;
​​
​​console.log(message);
​​
​​//Output
​​John has earned 5000 points

The code above is executed synchronously, each line is completed before moving on to the next line.
​​
​​Let's consider a scenario where you need to retrieve a large amount of data from an API and display it. By default, in JavaScript, the execution is blocked, and all further instructions are halted until the retrieval request is completed. This is where asynchronous programming becomes crucial. GIF

​​Let’s dive in.

​​What is Asynchronous JavaScript

​​Asynchronous JavaScript refers to the programming paradigm in which code execution does not follow the usual sequential flow from top to bottom. Instead, asynchronous code allows certain operations to be initiated and completed separately without blocking the execution of other code.
​​
​​The concept of asynchronous JavaScript enables you to break down big complex projects into smaller tasks.

What is involved in Asynchronous JavaScript?

  • Web Browser features (Web API).
  • Callback queue.
  • Event loop.
  • Callback.
  • Async/Await.
  • Promise.

Web Browser Features (Web API)

Browser APIs, also known as web APIs, are pre-existing interfaces embedded within web browsers. These APIs offer built-in functionalities that can be leveraged within web applications.

Web APIs allow developers to access and interact with these additional functionalities using JavaScript. By utilizing web APIs, developers can seamlessly incorporate specific features into their codebase while minimizing code complexity. Examples of such features include making network requests and efficiently managing client-side storage.

Some web browser features include:

  • setTimeout()
The setTimeout() method allows you to run a block of code after a certain time delay. It is designed to execute the code once.

The syntax of setTimeout() is:

setTimeout(function, milliseconds);

Example:
function username() {
  console.log("John")
}

setTimeout(username, 5000)
console.log("Doe")

Output:
Doe
John
  • setInterval()
The setInterval() method repeats a block of code at every given timing event.

The syntax of setInterval() is :

setInterval(function, milliseconds);

Example:
// program to display a text using setInterval method
function orderItem() {
    console.log('I want a bag');
}

setInterval(orderItem, 1000);

Output:
I want a bag
I want a bag
I want a bag
I want a bag
...

In the above program, the orderItem() function is invoked by the setInterval() method at an interval of 1000 milliseconds. Consequently, the program outputs the message "I want a bag" every 1 second.
  • clearTimeout()
clearTimeout() method cancels a timeout previously established by calling setTimeout().

The syntax of clearTimeout() is:

clearTimeout(timeoutId)

Example:
// Set a timeout that logs a message after 3 seconds
const timeoutId = setTimeout(() => {
  console.log("Timeout executed!");
}, 3000);

// Clear the timeout before it executes
clearTimeout(timeoutId);

Output:
If you run the code snippet provided, there will be no output. The clearTimeout() function cancels the execution of the timeout set by setTimeout() preventing the callback function from being invoked. As a result, the message “Timeout executed!" will not be logged to the console.
  • clearInterval()
clearInterval() is used If you want to stop the function call.

The syntax of clearInterval() is:

clearInterval(intervalId);

Example:
// Define a variable to store the interval ID
let intervalId;

// Function to be executed repeatedly
function showMessage() {
  console.log("Hello!");
}

// Start the interval
intervalId = setInterval(showMessage, 1000);

// After 5 seconds, stop the interval
setTimeout(() => {
  clearInterval(intervalId);
  console.log("Interval stopped");
}, 5000);

Output:
Hello!
Hello!
Hello!
Hello!
Interval stopped

In the above program, you first declare a variable intervalId to store the ID returned by setInterval function. The showMessage function is defined, which will be executed repeatedly every 1000 milliseconds (1 second) using setInterval.
After 5 seconds (5000 milliseconds), clearInterval is called with the intervalId as the argument to stop the interval. This will stop the execution of the showMessage function. Finally, a message is logged to the console indicating that the interval has been stopped.
  • fetch()
The fetch() method in JavaScript is used to request data from a server.

They are not part of the JavaScript language, but they are built on top of the core JavaScript language, providing you with extra superpowers to use in your JavaScript code.

Read more about Web APIs 👉 here 👈.

Callback queue

The callback queue is where the callback function is pushed and awaits execution. The callback queue follows the First-In-First-Out(FIFO) principle.

Now let’s go through this example:

function username() {
  console.log("John")
}

setTimeout(username, 5000)
console.log("Doe")
//output
Doe
John

This block of code demonstrates the usage of the setTimeout() function to delay the execution of a callback function.

Here's how it works:

  • The username function is defined and stored in the Global memory, which logs the string "John" to the console.
  • The setTimeout() function is called with two arguments. The first argument is the username function, which specifies the callback function to be executed, and the second argument is the delay in milliseconds, which in this case is 5000 milliseconds (or 5 seconds).
  • After calling setTimeout(), the code continues to the next line and logs the string "Doe" to the console.
  • The string "Doe" is logged to the console first.
  • After the delay of 5000 milliseconds, the username function is executed and logs the string "John" to the console.

Event Loop

The event loop continuously checks if the call stack is empty. If the call stack is empty, it means that there are no functions currently being executed. In that case, the loop takes the functions waiting in the callback queue and pushes them onto the call stack for execution. The event loop acts like a gatekeeper for the callback queue.

The event loop manages code execution to avoid blocking, enabling the program to proceed with its operation even during the completion of asynchronous tasks.

What is Callback?

When you pass a function as an argument to another function, and that function is invoked or executed within the outer function, it is commonly referred to as a callback function.

Callback Illustration

Now let’s go through this example:

function placeOrder() {
    setTimeout(() => {
       return (Math.random() * 10) <= 5 ? 'Bag' : 'Shoe';
    }, 2000);
}

let order = placeOrder();
console.log('Order is for: ' + order);

//Output
// Order is for: undefined

Here in the placeOrder function, the setTimeout() will run after 2 seconds, and by that time the console.log statement has already been executed, the printed value of order is undefined.

Now, you can resolve this issue by logging your message to the console only after the data has returned from placeOrder. This can be done by passing a callback function to placeOrder, which will be invoked inside the placeOrder function.

function placeOrder(callback) {
    setTimeout(() => {
        const order = (Math.random() * 10) <= 5 ? 'Bag' : 'Shoe';
        callback(order);
    }, 2000);
}

placeOrder((order) => {
    console.log('Order is for: ' + order);
});

//Output
// Order is for: Bag

After two seconds, the callback function will be called, and the console statement will get executed with the correct order value.
The output of placeOrder function may differ in your case as you are using Math.random() to decide order value.

Promise

Promise is an object in JavaScript that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

Promises simplify handling asynchronous code, making it easier to write and maintain asynchronous operations without resorting to callback functions or nested callbacks (known as callback hell).

Callback hell is a term used to describe a situation where the code becomes hard to read and understand due to a large number of nested callback functions.

A promise can be in one of three states:

  • Pending: The initial state when a promise is created, and the asynchronous operation is still ongoing.
  • Fulfilled: The state when the asynchronous operation is successfully completed, and the promise has a resolved value.
  • Rejected: The state when the asynchronous operation encounters an error or fails, and the promise has a reason for rejection.

There are three main aspects of promises.

  • Creating a promise
  • Handling a promise
  • Chaining of promise

Creating Promises

To create a promise, you use the Promise constructor, which accepts a function (commonly referred to as the executor) as an argument. The executor function is provided with two parameters: resolve and reject. Within the executor function, you execute your asynchronous task and invoke either resolve or reject based on the result.

Here's an example of creating a promise :

const fetchData = new Promise((resolve, reject) => {
  // Simulating asynchronous task (e.g., making an API call)
  setTimeout(() => {
    const data = { id: 1, name: "John Doe" };
   // Fulfilled the promise with the fetched data
    resolve(data); 
    reject(new Error("Failed to fetch data")); 
    // Alternatively, reject the promise with an error
  }, 2000);
});

Handling a Promise

After creating a promise, you can assign callbacks to handle the result using the then() and catch() methods.

The then() method takes a callback function as an argument and manages the fulfilled promise. It receives the resolved value or result of the promise as an argument.

The catch() method is used to handle the rejection of a promise and it is called when the promise is rejected, allowing you to handle any errors that occurred during the asynchronous operation.

Now let's go through this example:

fetchData
  .then((data) => {
    // Handle the fulfilled promise (access the resolved value)
    console.log("Fetched data:", data);
  })
  .catch((error) => {
    // Handle the rejected promise (access the error)
    console.log("Error:", error);
  });

Chaining Promises

Using the then() method, you can chain promises together to create a sequence of asynchronous operations. Each then() callback produces a new promise, enabling you to handle the outcome of the previous operation and proceed with the next one. Chaining promises allows you to establish a more sequential and readable flow of asynchronous operations.

fetchData
  .then((data) => {
    // Handle the first fulfilled promise
    console.log("Fetched data:", data);
    return someOtherAsyncTask(); 
 // Return a new promise
  })
  .then((result) => {
    // Handle the result of the second fulfilled promise
    console.log("Second async task result:", result);
  })
  .catch((error) => {
    // Handle any errors in the chain
    console.log("Error:", error);
  });

Async/Await

Async/Await is a relatively recent addition to JavaScript, introduced in ES8, that provides a means to write asynchronous code in a synchronous manner.

If you use the Async keyword before the function definition, you can then use await within the function. Await gives you the power to pause the function in a non-blocking way until the promise is resolved.

Now let's go through this example:

async function fetchData() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    const data = await response.json();
    console.log('Fetched data:', data);
  } catch (error) {
    console.log('Error fetching data:', error);
  }
}
fetchData();

Here’s the explanation to the code snippet:
  • An async function called fetchUserData() is declared. Inside this function, an asynchronous HTTP request is made to the API endpoint using the fetch() function. The fetch() function returns a promise that resolves to a response object.
  • The execution is paused using the await keyword, allowing the code to wait for the promise returned by fetch() to resolve. The resolved response object is stored in the response variable.
  • Another await keyword is used to pause the execution and wait for the promise returned by the response.json() method. This method parses the response body as JSON. The parsed JSON data is then stored in the data variable.
  • Finally, the fetched user data is logged to the console. If any errors occur during the execution of the async function, they are caught in the catch block, enabling the handling and logging of the errors.

Conclusion

In this article, you’ve learned what asynchronous JavaScript is and how to write asynchronous JavaScript using promises and async/await. You’ve also seen how to send requests using the fetch API and async/await and how to return a response to asynchronous calls.

In JavaScript, synchronous instructions always take precedence over asynchronous instructions. For instance, if you have a scenario with numerous console.log statements followed by a setTimeout() function with a duration of 0 milliseconds, all the console.log statements will be executed first before the setTimeout().

I hope this article was informative? You can give it a like or comment on what you think.

Adeola Ajiboso is available on TwitterLinkedIn, or GitHub. Keep an eye out for my upcoming blog post, in which I'll go over another important area of web development. As a developer, I'm glad to provide additional information. Until then, happy coding, and take care!

If you find this post exciting, find more exciting posts on Learnhub Blog; we write everything tech from Cloud computing to Frontend DevCybersecurityAI, and Blockchain.

Resources

Here are some blogs/posts I read as a reference. You should check them out:


💖 💪 🙅 🚩
scofieldidehen
Scofield Idehen

Posted on July 24, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Mystery of Closures in JavaScript!
javascript Mystery of Closures in JavaScript!

November 23, 2024

JS Variables, Operators, Data Types
javascript JS Variables, Operators, Data Types

July 10, 2024

JS Introduction
javascript JS Introduction

July 10, 2024

JS Function, Object, String
javascript JS Function, Object, String

July 11, 2024

My first node API
javascript My first node API

March 23, 2024