async/await in JavaScript

Introduction

async/await is syntax for working with Promises without long .then chains. An async function always returns a Promise; await pauses that function until a Promise settles. This chapter focuses on patterns, pitfalls, and parallel vs sequential flows used in real apps.

Prerequisites

Basic async Function

javascript
// async always returns a Promise
async function doubleAfterDelay(n) {
  await new Promise((resolve) => setTimeout(resolve, 50));
  return n * 2;
}
 
const result = await doubleAfterDelay(3);
console.log(result);

Top-level await works in ES modules (type: "module" or .mjs).

try/catch with await

javascript
async function loadProfile(id) {
  try {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    console.error("loadProfile:", err.message);
    return null;
  }
}
 
console.log(await loadProfile(1));

Rejected Promises from await throw inside try like sync exceptions.

Sequential vs Parallel

javascript
// Sequential — slower total time
async function sequential() {
  const a = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  const b = await fetch("https://jsonplaceholder.typicode.com/todos/2");
  return [await a.json(), await b.json()];
}
 
// Parallel — start together
async function parallel() {
  const [resA, resB] = await Promise.all([
    fetch("https://jsonplaceholder.typicode.com/todos/1"),
    fetch("https://jsonplaceholder.typicode.com/todos/2"),
  ]);
  return [await resA.json(), await resB.json()];
}
 
const t0 = Date.now();
await parallel();
console.log("parallel ms", Date.now() - t0);

Use Promise.all when tasks are independent.

Promise.allSettled for Partial Success

javascript
async function fetchMany(urls) {
  const results = await Promise.allSettled(
    urls.map((url) => fetch(url).then((r) => r.json()))
  );
 
  return results.map((r) =>
    r.status === "fulfilled" ? r.value : { error: r.reason.message }
  );
}
 
console.log(
  await fetchMany([
    "https://jsonplaceholder.typicode.com/todos/1",
    "https://invalid.example/data",
  ])
);

Async in Array Methods

javascript
// Wrong: forEach ignores async — callbacks fire without awaiting
const ids = [1, 2, 3];
 
// Right: for...of awaits each step
async function loadAllSequential(ids) {
  const out = [];
  for (const id of ids) {
    const res = await fetch(
      `https://jsonplaceholder.typicode.com/todos/${id}`
    );
    out.push(await res.json());
  }
  return out;
}
 
// Or parallel
async function loadAllParallel(ids) {
  const resList = await Promise.all(
    ids.map((id) =>
      fetch(`https://jsonplaceholder.typicode.com/todos/${id}`).then((r) =>
        r.json()
      )
    )
  );
  return resList;
}

IIFE for Scripts Without Top-Level await

javascript
// Classic script without module
(async () => {
  const data = await fetch("https://jsonplaceholder.typicode.com/todos/1").then(
    (r) => r.json()
  );
  console.log(data);
})();

Mini Example: Retry with await

javascript
async function withRetry(fn, max = 3) {
  for (let attempt = 1; attempt <= max; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (attempt === max) throw err;
      await new Promise((r) => setTimeout(r, 200 * attempt));
    }
  }
}
 
await withRetry(async () => {
  const res = await fetch("https://example.com");
  if (!res.ok) throw new Error("bad status");
  return res.status;
});

FAQ

await blocks the whole program?

Only the current async function—other events and callbacks still run on the main thread.

Return value of async?

Wrapping in Promise.resolvereturn 5 becomes Promise fulfilled with 5.

Mix await and .then?

Valid but pick one style per function for readability.

What comes next?

Web Audio API.