Back to Blog
Lesson 42 of the JavaScript: From Zero to Interactive Web Pages course
JavaScriptAugust 30, 20263 min read

Mastering Async and Await in Modern JavaScript

Learn to use async and await to write cleaner, more readable asynchronous JavaScript. Simplify your fetch logic and handle complex data flows with ease.

javascriptasyncawaitfetchweb-development
Vibrant JavaScript code displayed on a screen, highlighting programming concepts and software development.

Previously in this course, we explored Introduction to Promises: Mastering Async JavaScript Flow and implemented data fetching using the .then() chain. While that approach works, it can quickly lead to "callback hell" or deeply nested chains. In this lesson, we are introducing async and await, which provide a cleaner, more intuitive way to work with asynchronous code in modern JS.

Understanding Async and Await from First Principles

At its core, async/await is "syntactic sugar" built on top of Promises. It doesn't change how JavaScript handles tasks under the hood—the browser still performs network requests in the background—but it allows you to write your code in a linear, top-to-bottom style.

  • The async keyword: Placing async before a function declaration ensures that the function always returns a Promise.
  • The await keyword: This pauses the execution of an async function until the Promise is resolved. It "unwraps" the Promise, giving you the result directly.

If you ever find yourself struggling with performance because you've accidentally made your operations strictly serial, check out Fixing JavaScript Async Await Performance Bottlenecks to learn how to run multiple tasks concurrently.

Refactoring Fetch Logic: A Worked Example

In our weather dashboard, we previously used a chain of .then() calls to fetch data. Here is how that looks compared to the modern async/await approach.

The Old Way (Promise Chains)

JAVASCRIPT
function getWeather(url) {
  fetch(url)
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error("Error:", error));
}

The Modern Way (Async/Await)

JAVASCRIPT
async function getWeather(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}

Notice how the async/await version reads like standard, synchronous code. We use a try/catch block to handle errors, which is the standard way to catch rejected Promises when using this syntax.

Hands-on Exercise: Refactor Your Weather Service

Your current task is to revisit the fetch logic you wrote in Building the Weather Service: Fetching and Extracting Data.

  1. Open your weather.js file.
  2. Change your fetch function declaration to include the async keyword.
  3. Replace your .then() chains with const result = await fetch(...).
  4. Wrap your network logic in a try/catch block to ensure any network failures are caught gracefully.

Common Pitfalls

  • Forgetting the async wrapper: You can only use await inside a function marked as async. If you try to use it at the top level of your script (unless you are using ES Modules), you will get a syntax error.
  • Ignoring the try/catch block: Without it, an error in your network request will crash your logic, and you won't have a clean way to handle it. Always wrap your await calls in try/catch.
  • Over-awaiting: Avoid awaiting things that don't need to be awaited. If you have two independent requests, you can trigger them both before awaiting them, which keeps your application fast.

Frequently Asked Questions

Does async/await make my code run faster? No. It makes the code cleaner and easier to read. The actual network performance remains the same as using standard Promises.

Can I mix .then() and async/await? While you technically can, it is considered bad practice. Stick to one style within a single function to keep your codebase consistent and readable.

What happens if I forget the await keyword? If you forget await, the variable will store the Promise object itself rather than the resolved data. You'll likely see [object Promise] or errors when you try to access data properties.

Recap

In this lesson, we transitioned from Promise chains to the more readable async/await syntax. By using async to define our functions and await to pause for results, we've made our weather dashboard logic much easier to debug and maintain. This modern approach is the industry standard for handling asynchronous tasks in production environments.

Up next: We will begin integrating our logic into the final dashboard layout, ensuring our HTML and JS work in perfect harmony.

Similar Posts