Skip to main content

Command Palette

Search for a command to run...

Mastering Async/Await in JavaScript

Updated
4 min read
Mastering Async/Await in JavaScript

Async/await has transformed the way we handle asynchronous code in JavaScript. By simplifying the syntax, it allows developers to write cleaner and more readable code, which is a huge win for anyone dealing with complex asynchronous operations. Let’s explore why async/await was introduced and how it works, while also comparing it with promises and discussing error handling in async code.

Why async/await was introduced

JavaScript has always had a love-hate relationship with asynchronous operations. Before async/await, developers relied heavily on callbacks and promises. Callbacks led to what many called "callback hell," making code hard to read and maintain. Promises improved this situation but still left developers with nested structures that could confuse anyone. The introduction of async/await in ECMAScript 2017 aimed to address these pain points, allowing for a more synchronous-like code style while still handling asynchronous tasks. Imagine you’re working on a project for a Himachali restaurant called "Kullu Cuisine" that fetches menu items and reviews asynchronously. Async/await simplifies that process, making it feel more like a straightforward linear flow.

How async functions work

An async function is a function that always returns a promise. When you define a function with the async keyword, you can use await inside that function. This means that the function pauses execution until the awaited promise is resolved. This is like waiting for a cricket match to finish before you start discussing the highlights. For instance, consider a scenario where "Raj" wants to fetch the latest reviews for "Kullu Cuisine":

async function fetchReviews() {
  const response = await fetch('https://api.kullucuisine.com/reviews');
  const reviews = await response.json();
  return reviews;
}

When fetchReviews is called, it will wait for the fetch call to resolve before proceeding to the next line of code. This makes it clear and easy to follow.

Await keyword concept

The await keyword can only be used inside async functions and is a way to pause the execution until the promise resolves. It’s like waiting at the top of a hill in Himachal Pradesh for a friend to catch up before you start the trek down. Using await can significantly reduce the complexity of your code. For example, let’s say Ayush wants to get his favorite dishes from the menu and their prices:

async function fetchMenu() {
  const response = await fetch('https://api.kullucuisine.com/menu');
  const menu = await response.json();
  return menu;
}

In this case, Ayush can wait for the menu to load before doing anything else with it. This linear approach is more intuitive than chaining .then() calls.

Error handling with async code

Error handling can feel daunting, especially with asynchronous code. With async/await, you can use try/catch blocks to manage errors more cleanly. Let’s say the fetch call fails for some reason, like a server issue while retrieving the menu:

async function fetchMenu() {
  try {
    const response = await fetch('https://api.kullucuisine.com/menu');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const menu = await response.json();
    return menu;
  } catch (error) {
    console.error('Fetch error:', error);
  }
}

This approach allows for graceful error handling. If something goes wrong, Ayush can see the error message without crashing the entire application.

Comparison with promises

While promises were a major improvement over callbacks, they still required chaining methods and could become unwieldy. Async/await provides a cleaner syntax. Consider this promise-based code:

fetch('https://api.kullucuisine.com/menu')
  .then(response => response.json())
  .then(menu => console.log(menu))
  .catch(error => console.error('Fetch error:', error));

Now, compare that with the async/await version:

async function showMenu() {
  try {
    const response = await fetch('https://api.kullucuisine.com/menu');
    const menu = await response.json();
    console.log(menu);
  } catch (error) {
    console.error('Fetch error:', error);
  }
}

The async/await version looks much cleaner and reads like synchronous code. This makes it easier to follow the logic, especially as your application grows.

In conclusion, async/await has made JavaScript asynchronous code much more manageable. It allows developers like Raj and Ayush to write cleaner code, handle errors gracefully, and improve overall readability. As you continue to build applications, consider incorporating async/await to enhance your coding experience.