JavaScript async/await

Wait for promises in a readable, top-to-bottom style with try/catch.

Lesson 49 of 56Advanced13 min read

Promises are powerful, but long chains of .then() calls can become hard to follow. async/await lets you write asynchronous code that reads like normal code from top to bottom, while the page still never freezes. It's how most modern JavaScript loads and sends data.

async/await isn't a replacement for promises; it's a nicer way of using them. Everything you learned in the Promises lesson still applies underneath. The difference is that your code describes the steps in order, which makes it easier to read, debug and change.

async functions

Put async before a function and it always returns a Promise:

JavaScript
async function getNumber() {
  return 42;
}
getNumber().then(n => console.log(n)); // 42

It works with arrow functions too: const load = async () => { … };. Even if you return a plain value, the caller receives a promise for that value.

await: waiting for a result

Inside an async function, await pauses that function until a promise settles, then gives you its value:

JavaScript
async function showUser() {
  const user = await fetchUser();   // wait here…
  console.log(user.name);            // …then carry on
}

Only the async function pauses. The rest of the page keeps working, so buttons still respond and animations keep running.

Forgetting await is a common mistake. Without it, the variable holds the promise itself rather than the result, and you'll see Promise { <pending> } in the console.

Handling errors with try...catch

If an awaited promise is rejected, await throws an error. Handle it with the familiar try...catch:

JavaScript
async function load() {
  try {
    const data = await getData();
    show(data);
  } catch (error) {
    showError(error.message);
  } finally {
    hideSpinner();
  }
}

This is the same structure you learned in Errors, which is one of the main advantages of async/await.

Running tasks in sequence or in parallel

Using await on one task after another runs them in sequence, with each waiting for the previous one. If the tasks don't depend on each other, start them together:

JavaScript
const [user, posts] = await Promise.all([getUser(), getPosts()]);

Two requests that each take half a second finish in about half a second together, instead of a full second one after the other.

JavaScript async/await Syntax

async/await

async function name() {
  try {
    const value = await somePromise;
    return value;
  } catch (error) {
    // handle
  }
}

const arrow = async () => { await thing(); };

JavaScript async/await Summary

  • An async function always returns a promise.
  • await pauses only the async function until a promise settles.
  • Forgetting await leaves you with a promise instead of a value.
  • Use try...catch...finally to handle errors from awaited promises.
  • Use Promise.all to run independent tasks in parallel.

JavaScript async/await Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

then vs await

Both functions do the same thing. Which is easier to read?

const wait = (ms, v) => new Promise(r => setTimeout(() => r(v), ms));

function withThen() {
  return wait(300, "🥚")
    .then(egg => wait(300, egg + "🍳"))
    .then(meal => console.log("then:", meal));
}

async function withAwait() {
  const egg = await wait(300, "🥚");
  const meal = await wait(300, egg + "🍳");
  console.log("await:", meal);
}

withThen();
withAwait();
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 3 to 7A promise chain.
  2. Lines 9 to 13The same steps with await, which read from top to bottom.
Example 2

Handling failure

Run it a few times.

function getWeather() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      Math.random() > 0.4
        ? resolve({ temp: 19, sky: "☀️" })
        : reject(new Error("Weather service is down"));
    }, 400);
  });
}

async function showWeather() {
  console.log("Loading…");
  try {
    const w = await getWeather();
    console.log(`Today: ${w.temp}°C ${w.sky}`);
  } catch (error) {
    console.log("Sorry:", error.message);
  } finally {
    console.log("Done loading");
  }
}

showWeather();
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 8A promise that randomly succeeds or fails.
  2. Line 14await gives the value, or throws if the promise was rejected.
  3. Lines 16 to 17catch handles the failure.
  4. Lines 18 to 20finally always runs.
Example 3

Sequential vs parallel timing

const task = (name, ms) => new Promise(r => setTimeout(() => r(name), ms));

async function sequential() {
  const t = Date.now();
  await task("A", 400);
  await task("B", 400);
  console.log("Sequential:", Date.now() - t >= 790 ? "~800ms" : "?");
}

async function parallel() {
  const t = Date.now();
  await Promise.all([task("A", 400), task("B", 400)]);
  console.log("Parallel:", Date.now() - t < 600 ? "~400ms" : "?");
}

sequential();
parallel();
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 3 to 8Each await waits for the previous task, so it takes 800ms in total.
  2. Lines 10 to 14Both tasks start together, taking about 400ms.

Common JavaScript async/await Mistakes

await outside an async function

Don't
function load() {
  const data = await getData(); // SyntaxError
}
Do
async function load() {
  const data = await getData();
}

Forgetting await

Don't
async function show() {
  const user = getUser();   // a Promise!
  console.log(user.name);   // undefined
}
Do
async function show() {
  const user = await getUser();
  console.log(user.name);
}

Awaiting in a loop when you don't need to

Don't
for (const id of ids) {
  results.push(await getItem(id)); // one at a time
}
Do
const results = await Promise.all(ids.map(id => getItem(id)));

When requests don't depend on each other, run them in parallel.

JavaScript async/await in Real Projects

async/await in practice

Loading a page's data, submitting a form to a server, uploading a photo and reading from a database in Node.js are almost always written with async/await today, usually wrapped in try...catch with a loading state shown on the page.

JavaScript async/await Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Make the function async and wait for the data.

 function start() {
  const data =  loadData();
  console.log(data);
}
Multiple choicemedium

What does an async function always return?

Predict the outputmedium

In what order are the letters logged?

async function run() {
  console.log("B");
  await null;
  console.log("D");
}
console.log("A");
run();
console.log("C");
Fix the bugmedium

getTotal should wait for both prices and log Total: 30. Fix it.

const price = (n) => new Promise(r => setTimeout(() => r(n), 100));

function getTotal() {
  const a = price(10);
  const b = price(20);
  console.log("Total:", a + b);
}

getTotal();
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • Logs "Total: 30"
  • getTotal is async

JavaScript async/await Quiz

3 questions to check what you learned.

  1. Where can you use await in a normal (non-module) script?

    • Only inside async functions
    • Anywhere
    • Only in loops
    • Only with fetch
  2. How do you handle errors from an awaited promise?

    • .error()
    • if (await)
    • They can't be handled
    • try…catch
  3. How do you run two independent awaits at the same time?

    • await Promise.all([a(), b()])
    • await twice in a row
    • await a() && await b()
    • It's impossible

JavaScript async/await Challenge

Load a dashboard

Using the fake api functions:

  1. Write async function loadDashboard() that awaits getUser() and getTasks() in parallel with Promise.all
  2. Log Hi Rae, you have 3 tasks
  3. Return the number of tasks
const api = (value, ms) => new Promise(r => setTimeout(() => r(value), ms));
const getUser = () => api({ name: "Rae" }, 200);
const getTasks = () => api(["Email", "Gym", "Read"], 300);
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • loadDashboard resolves to 3
  • Logs the greeting
  • Uses Promise.all