Technology · JavaScript

JavaScript Async and Await

Handle async code cleanly with async functions, await, error handling, and parallel execution.

TL;DR
  1. 01Mark functions async to make them return promises.
  2. 02Use await to pause and unwrap promise values.
  3. 03Wrap awaited code in try-catch for error handling.

Async Functions

  • Mark a function async to make it always return a promise.
    async function fetchData() {
      return "data";
    }
    fetchData().then(result => console.log(result));
  • An async function that returns a value wraps it in a resolved promise.
  • You can use await only inside async functions.
    async function getData() {
      const response = await fetch("/api/data");
      return response.json();
    }
  • Async functions are just a cleaner way to work with promises.
  • Every async function returns a promise, even if it doesn't use await.

Await Keyword

  • Use await to pause execution until a promise resolves.
    async function getUser(id) {
      const response = await fetch(`/api/users/${id}`);
      const user = await response.json();
      return user;
    }
  • Await unwraps the resolved value from a promise automatically.
  • Await can only be used inside async functions or at module top level.
  • Each await pauses the function until that promise settles.
    async function getMultiple() {
      const a = await fetch("/a"); // waits
      const b = await fetch("/b"); // waits for /a first
      return [a, b];
    }
  • Do not use await for independent operations — use Promise.all instead.

Error Handling

  • Wrap await calls in try-catch to handle rejection errors.
    async function fetchData() {
      try {
        const res = await fetch("/data");
        return await res.json();
      } catch (error) {
        console.error("Failed to fetch:", error);
        return null;
      }
    }
  • Catch blocks run when any await in the try block rejects.
  • Use finally to run cleanup code regardless of success or failure.
    async function withCleanup() {
      try {
        return await operation();
      } finally {
        cleanup();
      }
    }
  • Throw errors from async functions to propagate them as promise rejections.
  • Attach .catch() to an async call when you prefer promise chaining.
    fetchData()
      .then(data => process(data))
      .catch(error => console.error('Failed:', error.message));

Parallel Execution

  • Use Promise.all() to await multiple independent operations in parallel.
    async function getUsers() {
      const [user1, user2] = await Promise.all([
        fetch("/users/1").then(r => r.json()),
        fetch("/users/2").then(r => r.json())
      ]);
      return [user1, user2];
    }
  • This is much faster than awaiting sequentially.
  • Use Promise.race() to get the first result from multiple promises.
    const fastest = await Promise.race([api1(), api2()]);
  • Use Promise.allSettled() when you want all results, even if some fail.
  • Use Promise.any() to resolve as soon as the first promise succeeds.
    const result = await Promise.any([
      fetch('/endpoint-1').then(r => r.json()),
      fetch('/endpoint-2').then(r => r.json())
    ]);
    // Resolves with whichever responds first without rejecting

Common Patterns

  • Top-level await in modern JavaScript (ES2022+) for script initialization.
    // In a module
    const config = await loadConfig();
  • Chain multiple async operations with proper error handling.
    async function workflow() {
      const data = await fetch1();
      const result = await process(data);
      return await fetch2(result);
    }
  • Use async IIFE for immediate async execution without named functions.
    (async () => {
      const data = await fetchData();
      console.log(data);
    })();
  • Loop over async items with for-of and async iteration.
    for await (const item of asyncIterator()) {
      console.log(item);
    }

Tip: Use Promise.all() for independent async operations to run them in parallel and get faster results.

Warning: Awaiting operations sequentially when they are independent will be slower than using Promise.all() for parallel execution.

JavaScript Arrow FunctionsJavaScript Closures