JAVASCRIPT

JavaScript Async Await: Syntax, Usage, and Examples

JavaScript async await syntax allows asynchronous code to look and behave more like synchronous code. Introduced in ES2017, async/await simplifies working with Promises and makes your code easier to read, write, and debug — especially in complex workflows involving API calls, timers, or file access.


How to Use JavaScript Async Await

To use async await JavaScript provides, start by defining a function with the async keyword. Inside that function, use await to pause execution until a Promise resolves or rejects.

Here’s the basic pattern:

async function fetchData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
}
  • async marks a function as asynchronous. It always returns a Promise.
  • await pauses the function’s execution until the Promise on the right resolves.
  • You can only use await inside async functions (or in the top level of supported environments like modern browsers or Node.js with ES modules).

The result: cleaner, flatter code without .then() chains or nested callbacks.


When to Use Async Await in JavaScript

1. Chaining Multiple Asynchronous Operations

When you need to perform one async task after another, async and await in JavaScript simplify the flow. For example, you may want to:

  • Fetch user data
  • Then fetch posts based on that user
  • Then render both in the UI

Using async/await, the logic is sequential and readable.

2. Reducing Callback Complexity

Async/await helps flatten callback hell — the pyramid of nested functions that occurs with traditional asynchronous code. This makes the program easier to follow and less error-prone.

3. Handling API Errors Gracefully

You can wrap await calls in try/catch blocks to catch errors just like synchronous code, making error handling consistent and intuitive.

async function safeFetch() {
  try {
    const response = await fetch("https://bad.url");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Fetch failed:", error);
  }
}

This is much easier to manage than nested .catch() chains.


Examples of Async Await JavaScript Usage

1. Sequential API Requests

async function loadDashboard() {
  const user = await fetch("/api/user").then(res => res.json());
  const stats = await fetch(`/api/stats/${user.id}`).then(res => res.json());

  console.log("User:", user);
  console.log("Stats:", stats);
}

Each await waits for the previous call to complete before continuing.

2. Running Asynchronous Functions in Parallel

Sometimes you don’t need to wait for each task to finish before starting the next. You can launch Promises simultaneously, then await them together with Promise.all().

async function getData() {
  const userPromise = fetch("/api/user");
  const postsPromise = fetch("/api/posts");

  const [userRes, postsRes] = await Promise.all([userPromise, postsPromise]);
  const user = await userRes.json();
  const posts = await postsRes.json();

  console.log("User:", user);
  console.log("Posts:", posts);
}

This pattern improves performance by allowing multiple tasks to run concurrently.

3. Handling Rejected Promises

async function riskyOperation() {
  try {
    const result = await fetch("/api/broken-endpoint");
    const json = await result.json();
    console.log(json);
  } catch (error) {
    console.error("Something went wrong:", error.message);
  }
}

You can use try/catch to handle rejections just like you would handle errors in regular synchronous code.


Learn More About Async Await in JavaScript

What Is Async Await in JavaScript Exactly?

Async/await in JavaScript is syntactic sugar over Promises. It doesn’t replace Promises — it enhances how you work with them.

  • async functions always return a Promise. Even if you return a plain value, it will be wrapped in a resolved Promise.
  • await pauses the execution of the async function until the Promise is settled (either resolved or rejected).

This model helps simplify asynchronous control flow, especially when dealing with multiple dependent operations.

How Async Functions Work Behind the Scenes

When you call an async function, it runs until the first await, pauses, and schedules the rest of the function to run when the awaited Promise resolves. This happens through the microtask queue in the JavaScript runtime.

console.log("Start");

async function test() {
  console.log("Inside function");
  await Promise.resolve();
  console.log("After await");
}

test();

console.log("End");

// Output:
// Start
// Inside function
// End
// After await

The message After await runs after the main thread finishes executing, because the await pauses execution and lets the event loop continue.


Mixing Async Await with Traditional Promises

You can freely combine async functions with .then():

async function fetchAndLog() {
  await fetch("/api/data")
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.error(err));
}

This can be useful when working with third-party libraries that return Promises.

However, mixing styles too much can reduce readability. Prefer consistent use of await in async functions unless you have a specific reason to use .then() chains.

Using Async Await in Loops

When using await inside loops, be aware that it pauses the loop on each iteration:

const urls = ["/a", "/b", "/c"];

async function fetchAll() {
  for (const url of urls) {
    const res = await fetch(url);
    const data = await res.json();
    console.log(data);
  }
}

This pattern is simple but inefficient if the requests don’t depend on each other. For parallel processing, use Promise.all() or map() with await.

async function fetchAllFast() {
  const results = await Promise.all(urls.map(url =>
    fetch(url).then(res => res.json())
  ));
  console.log(results);
}

This version runs all requests at the same time and waits for all to finish.


Top-Level Await

In modern environments that support ES modules, you can use await at the top level without wrapping it in a function.

// example.mjs
const response = await fetch("/api/data");
const data = await response.json();
console.log(data);

This works in:

  • ES modules in modern browsers
  • .mjs files in Node.js
  • Some interactive shells like Deno or browser DevTools

Top-level await makes quick scripts cleaner, but for compatibility, many production environments still use async functions.


Returning Values from Async Functions

Async functions return Promises, so you need to use await or .then() to access their results.

async function getNumber() {
  return 42;
}

getNumber().then(console.log); // Output: 42

Or use await inside another async function:

async function caller() {
  const num = await getNumber();
  console.log(num); // Output: 42
}

If you forget to await, you’ll log a Promise object instead of the value you expected.


Error Handling Best Practices

Some useful patterns for handling errors:

  1. Global try/catch:

async function load() {
  try {
    const data = await fetchData();
    display(data);
  } catch (e) {
    showError("Could not load data.");
  }
}
  1. Fail early with custom errors:

if (!userToken) {
  throw new Error("Missing user token");
}
  1. Graceful degradation with fallback values:

const data = await fetchData().catch(() => ({ fallback: true }));

Handling async errors well helps avoid crashes and improves user experience.


Async/Await and Event Handlers

In frontend development, it’s common to use async functions in response to user actions:

document.querySelector("#button").addEventListener("click", async () => {
  try {
    const data = await fetch("/api/data").then(res => res.json());
    console.log(data);
  } catch (e) {
    console.error("Error loading data:", e);
  }
});

Because event listeners can run async functions, you can keep UI interactions clean and responsive while still using asynchronous logic behind the scenes.


JavaScript async await offers a cleaner, more readable alternative to Promise chaining and callback-based code. You use async to define functions that return Promises, and await to pause execution until a Promise resolves. This pattern shines when dealing with sequential API calls, file operations, timers, or any task that relies on asynchronous events.

Learn to Code in JavaScript for Free
Start learning now
button icon
To advance beyond this tutorial and learn JavaScript by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH

Reach your coding goals faster