JavaScript async/await

Async Code That Reads Like Sync Code

JavaScript async/await

`async` functions and the `await` keyword let you write async code in a straight-line, top-to-bottom style — much easier to read than `.then` chains.

5 min read Level 3/5 #async#await#promises
What you'll learn
  • Mark a function `async`
  • `await` a Promise
  • Handle errors with `try`/`catch`

async/await is syntactic sugar over Promises. Mark a function async to use await inside it. The result is async code that reads like ordinary, top-to-bottom code.

async/await basics script.js
function delay(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function run() {
  console.log("start");
  await delay(50);
  console.log("after 50ms");
  await delay(50);
  console.log("after 100ms");
}

run();
▶ Preview: console

await pauses the function until the Promise resolves, then continues with the resolved value.

Returning From an async Function

An async function always returns a Promise. Whatever you return becomes the resolved value. Throwing becomes a rejection.

async returns a Promise script.js
async function getNumber() {
  return 42;
}

console.log(getNumber());           // Promise { 42 }
getNumber().then(console.log);      // 42
▶ Preview: console

await Unwraps Promises

If you await a non-Promise, it’s used as-is. If you await a Promise, you get its resolved value.

await unwraps script.js
async function example() {
  const a = await 42;           // 42 — non-Promise passes through
  const b = await Promise.resolve("hi");
  return [a, b];
}

example().then(console.log);   // [42, "hi"]
▶ Preview: console

Error Handling With try/catch

A rejected Promise becomes a thrown error inside an async function. Catch it with try/catch — same as synchronous code.

try/catch for async errors script.js
async function run() {
  try {
    const result = await Promise.reject(new Error("boom"));
    console.log(result);
  } catch (e) {
    console.log("caught:", e.message);   // "caught: boom"
  } finally {
    console.log("cleanup");
  }
}

run();
▶ Preview: console

This is the killer feature. With .then/.catch, error flow is implicit. With async/await, it’s the same try/catch you’ve been using since the start.

Parallel vs Sequential

await waits. If you await two independent things one after another, they run sequentially.

Sequential — slow script.js
function fetchSlow(label, ms) {
  return new Promise((r) => setTimeout(() => r(label), ms));
}

async function sequential() {
  const a = await fetchSlow("a", 100); // wait 100ms
  const b = await fetchSlow("b", 100); // then wait another 100ms
  return [a, b];                       // ~200ms total
}

sequential().then(console.log);
▶ Preview: console

For independent work, kick off the Promises first, then await them:

Parallel — fast script.js
function fetchSlow(label, ms) {
  return new Promise((r) => setTimeout(() => r(label), ms));
}

async function parallel() {
  const ap = fetchSlow("a", 100);   // started
  const bp = fetchSlow("b", 100);   // started, in parallel
  return [await ap, await bp];      // ~100ms total
}

parallel().then(console.log);
▶ Preview: console

Or use Promise.all:

Promise.all + await script.js
function fetchSlow(label, ms) {
  return new Promise((r) => setTimeout(() => r(label), ms));
}

async function withAll() {
  const [a, b] = await Promise.all([
    fetchSlow("a", 100),
    fetchSlow("b", 100),
  ]);
  return [a, b];   // ~100ms total
}

withAll().then(console.log);
▶ Preview: console

Top-Level await

Inside a module, you can await at the top level — no async wrapper needed.

data.js
const response = await fetch("/api/data");
export const data = await response.json();

Other modules that import from data.js will wait for its setup before running.

Try It Yourself

Exercise

Rewrite a Chain With async/await

Difficulty 2/5~5 min
Rewrite this `.then` chain as an `async` function called `loadGreeting` that returns the greeting string. Use `try`/`catch` to log `"oops"` if anything fails. ```js fetchUser(1) .then(u => `Hello, ${u.name}`) .then(g => g.toUpperCase()); ```
solution.js
function fetchUser(id) {
  return Promise.resolve({ id, name: "Ada" });
}

async function loadGreeting() {
  // your code
}
0tests will run
✅ Show solution
async function loadGreeting() {
  try {
    const u = await fetchUser(1);
    const g = `Hello, ${u.name}`;
    return g.toUpperCase();
  } catch {
    console.log("oops");
  }
}

Up Next

The most common async operation in web development: making an HTTP request with fetch.

JavaScript fetch →