Codectionary / Developer documentation / JavaScript

Async/Await

Async/await is modern JavaScript syntax for handling asynchronous operations, making asynchronous code look and behave like synchronous code. The async keyword marks a function as asynchronous, and await pauses execution until a Promise resolves. This approach makes asynchronous code more readable and easier to understand than traditional Promise chains or callbacks.

Syntax

async function name() {
  const result = await promise;
}

Examples

Basic Async/Await

Using async/await to handle promises in a cleaner way.

// Async function declaration
async function fetchUser() {
  const response = await fetch("https://api.example.com/user/1");
  const data = await response.json();
  return data;
}

// Using the async function
fetchUser()
  .then(user => console.log(user))
  .catch(err => console.error(err));

// Arrow function version
const getUser = async () => {
  const response = await fetch("https://api.example.com/user/1");
  return await response.json();
};

Error Handling

Using try/catch blocks to handle errors in async functions.

async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    console.log("Data received:", data);
    return data;
  } catch (error) {
    console.error("Failed to fetch data:", error.message);
    return null;
  } finally {
    console.log("Fetch attempt completed");
  }
}

fetchData();

Sequential vs Parallel

Understanding when to await sequentially vs run operations in parallel.

// Sequential - one after another (slower)
async function sequential() {
  const user = await fetch("/api/user");
  const posts = await fetch("/api/posts");
  const comments = await fetch("/api/comments");
  // Total time = sum of all requests
}

// Parallel - all at once (faster)
async function parallel() {
  const [user, posts, comments] = await Promise.all([
    fetch("/api/user"),
    fetch("/api/posts"),
    fetch("/api/comments")
  ]);
  // Total time = slowest request
}

// Conditional parallel
async function conditional() {
  const user = await fetch("/api/user");
  
  // Only after user is fetched, get these in parallel
  const [posts, friends] = await Promise.all([
    fetch(`/api/users/${user.id}/posts`),
    fetch(`/api/users/${user.id}/friends`)
  ]);
}

Real-World Example

Practical async/await usage in a complete data fetching scenario.

async function loadUserDashboard(userId) {
  try {
    console.log("Loading dashboard...");
    
    // Fetch user profile first
    const userResponse = await fetch(`/api/users/${userId}`);
    const user = await userResponse.json();
    console.log("User loaded:", user.name);
    
    // Then fetch related data in parallel
    const [posts, notifications, stats] = await Promise.all([
      fetch(`/api/users/${userId}/posts`).then(r => r.json()),
      fetch(`/api/users/${userId}/notifications`).then(r => r.json()),
      fetch(`/api/users/${userId}/stats`).then(r => r.json())
    ]);
    
    // Assemble dashboard data
    const dashboard = {
      user,
      posts,
      notifications,
      stats
    };
    
    console.log("Dashboard ready!");
    return dashboard;
  } catch (error) {
    console.error("Dashboard load failed:", error);
    throw new Error("Failed to load dashboard");
  }
}

// Usage
loadUserDashboard(123)
  .then(data => console.log(data))
  .catch(err => console.error(err));

Best practices

  • Always use try/catch blocks to handle errors in async functions
  • Use Promise.all() for parallel operations to improve performance
  • Don't forget that async functions always return a Promise
  • Avoid awaiting in loops - use Promise.all() with map instead
  • Add finally blocks for cleanup operations that should always run
  • Consider using Promise.allSettled() when you want all results regardless of failures

At a glance

Purpose
Application logic and interaction
File extension
.js ยท .mjs
Runs in
Browsers and JavaScript runtimes
Usually used with
HTML, CSS and Web APIs

Specifications & further reading

Related JavaScript documentation

Promises
A Promise represents a value that may not be available yet - the eventual result of an asynchronous operation. It exists in one of three states: pending, fulfilled, or rejected. then() handles a successful result, catch() handles an error, and finally() runs regardless of outcome. Promises are the foundation that async/await is built on top of.
Promise.all(), allSettled(), race(), any()
These static methods handle multiple promises at once. Promise.all() waits for every promise to resolve, but rejects immediately if any one fails. allSettled() waits for all to finish regardless of outcome, giving you both successes and failures. race() resolves or rejects as soon as the first promise settles. any() resolves as soon as the first one succeeds, ignoring rejections unless all fail.
Fetch API
The Fetch API provides a modern, promise-based way to make HTTP requests, replacing the older XMLHttpRequest. fetch() returns a promise that resolves to a Response object once headers are received - note that a Response is only considered "ok" if the status is 2xx, so checking response.ok is essential, since fetch() does not reject on HTTP error statuses.
Web Workers
Web Workers run JavaScript on a separate background thread, away from the main UI thread, so expensive computations do not freeze the page. Communication between the main script and a worker happens through message passing with postMessage() and the onmessage event handler, rather than shared memory - workers cannot directly access the DOM.