Course topics

By WebNest Studio Engineering Team

JavaScript Tutorial

Async/Await in JavaScript

async/await is syntax introduced in ES2017 that lets you write asynchronous code that reads like synchronous code, while still being built entirely on top of Promises. It doesn't replace Promises — it's "syntactic sugar" over them, making Promise-based code easier to write and reason about, especially when several async steps depend on each other.

Any function marked async automatically returns a Promise, and inside it, the await keyword pauses execution of that function (without blocking the rest of the program) until the awaited Promise settles.

Why it matters

async and await let asynchronous code be written top to bottom like ordinary code, with normal try and catch for errors. It is the form in which almost all API calls are written today. One common mistake is awaiting independent operations one after another when they could run together, which doubles the waiting time.

How await Relates to Promises

await somePromise is roughly equivalent to calling .then() on that Promise and continuing the function with the resolved value — except it looks like a normal, blocking assignment. If the awaited Promise rejects, await throws that rejection as a regular JavaScript exception at that line, which is why try/catch becomes the natural way to handle async errors instead of .catch() chains.

Error Handling with try/catch

Wrapping await expressions in a try block lets you catch failures exactly where synchronous code would: try { const data = await fetchData(); } catch (err) { /* handle it */ }. Without a surrounding try/catch, a rejected await propagates up as a rejected Promise from the async function itself, which the caller must then handle.

Sequential vs Parallel Awaiting

Awaiting one operation after another inside a loop or a sequence of statements runs them one at a time, which is correct when each step depends on the previous result, but wasteful when they're independent. To run independent async calls concurrently while still using await, start all the Promises first (without awaiting immediately) and then await Promise.all() on them together.

Examples

An async function with await and try/catch, alongside synchronous code

JavaScript
function fetchUserAge(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      id === 1 ? resolve(28) : reject(new Error('User not found'));
    }, 100);
  });
}

async function printUserInfo(id) {
  console.log('Start fetching...');
  try {
    const age = await fetchUserAge(id);
    console.log('Age is', age);
  } catch (err) {
    console.error('Failed:', err.message);
  }
  console.log('Done.');
}

printUserInfo(1);
console.log('This logs before "Age is 28" because await does not block.');
Output
Start fetching...
This logs before "Age is 28" because await does not block.
Age is 28
Done.

Sequential awaits vs concurrent awaits with Promise.all

JavaScript
const delay = (label, ms) =>
  new Promise((resolve) => setTimeout(() => resolve(label), ms));

async function sequential() {
  const a = await delay('A', 100); // waits 100ms
  const b = await delay('B', 100); // then waits another 100ms
  console.log(a, b, '- sequential took ~200ms total');
}

async function concurrent() {
  const pA = delay('A', 100); // both start immediately
  const pB = delay('B', 100);
  const [a, b] = await Promise.all([pA, pB]); // waits ~100ms total
  console.log(a, b, '- concurrent took ~100ms total');
}

sequential();
concurrent();
Output
A B - concurrent took ~100ms total
A B - sequential took ~200ms total

Common Mistakes

  • Marking a function async but forgetting that it always returns a Promise, then trying to use its return value directly instead of awaiting or chaining it.
  • Awaiting independent operations one after another instead of starting them together and awaiting Promise.all(), which needlessly slows down the total time.
  • Forgetting try/catch around await, letting a rejected Promise crash an unhandled path or silently reject the enclosing async function.
  • Using await outside of an async function (in older environments without top-level await support), which is a syntax error.

Exercise

fetchUser is a stand-in for an API call: it resolves with a user for id 1 and rejects for any other id. Write the async function loadName, which returns the user's name, or the text not found when the request fails.

Expected output: Asha then not found

JavaScript · starter code
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id === 1) resolve({ id: 1, name: "Asha" });
      else reject(new Error("no such user"));
    }, 10);
  });
}

async function loadName(id) {
  // TODO: await fetchUser(id) and return the name; return "not found" on failure
}

async function main() {
  console.log(await loadName(1));
  console.log(await loadName(2));
}

main();
Show hints
  • Put the await inside a try block; a rejected promise is thrown as an exception at the await.
  • Return "not found" from the catch block.
Show solution
JavaScript · solution
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id === 1) resolve({ id: 1, name: "Asha" });
      else reject(new Error("no such user"));
    }, 10);
  });
}

async function loadName(id) {
  try {
    const user = await fetchUser(id);
    return user.name;
  } catch (error) {
    return "not found";
  }
}

async function main() {
  console.log(await loadName(1));
  console.log(await loadName(2));
}

main();

Quick quiz

1. What does an async function always return?

  • A. undefined
  • B. A callback
  • C. A promise
  • D. The value directly
Show answer

C. A returned value becomes the fulfilled value of that promise.

2. What does await do?

  • A. Cancels the promise
  • B. Blocks the whole program
  • C. Creates a new thread
  • D. Pauses the async function until the promise settles, without blocking the rest of the program
Show answer

D. Other code, such as event handlers, keeps running while the function is paused.

3. Two independent requests each take one second. How should they be awaited to finish in about one second?

  • A. await Promise.all([first(), second()])
  • B. await first(); await second();
  • C. first(); second();
  • D. await first() + second()
Show answer

A. Awaiting them one after the other takes about two seconds.

Interview questions

How do async and await relate to promises?

Show answer

They are syntax built on promises. An async function always returns a promise. await pauses the function until the promise it is given settles, then gives back the fulfilled value or throws the rejection as an exception, which try and catch can handle. The code after an await behaves like the callback of a then. The result reads like synchronous code.

What are common mistakes with async and await?

Show answer

Forgetting await, so that a promise is used where its value was intended. Awaiting independent operations in sequence instead of with Promise.all. Using await inside forEach, which does not wait; a for...of loop or Promise.all with map is needed. And leaving out error handling, so that a rejection goes unhandled.

Key Points to Remember

  • async functions always return a Promise; await pauses only that function, not the whole program.
  • await is syntactic sugar over .then() — a rejected awaited Promise throws, so try/catch is the standard error-handling pattern.
  • Sequential awaits run one after another; starting Promises first and awaiting Promise.all() runs them concurrently.
  • async/await does not replace Promises — every async function is still built on the Promise mechanism underneath.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering