DevAcademy
LearnJavaScriptAsync/Await
AdvancedJavaScript

Async/Await

Learn how async/await lets you write asynchronous, Promise-based code that reads like ordinary synchronous code, including error handling and running work in parallel.

Reading Time

20 min

Lesson

Lesson 31 of 48

From Promise Chains to async/await

async/await is syntax built on top of Promises — it doesn't replace them, it makes them easier to read and write. Instead of chaining .then() calls, you mark a function as async and use the await keyword inside it to pause execution until a promise settles. The result reads almost exactly like synchronous code, while still being fully asynchronous under the hood.

async Functions Always Return a Promise

Adding the async keyword before a function declaration changes what it returns: an async function always returns a Promise, no matter what you return inside it. If you return a plain value, JavaScript automatically wraps it in a resolved promise. If you throw an error inside an async function, that error becomes a rejected promise instead of an uncaught exception.

async Functions Wrap Return Values in Promises

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

The await Keyword

The await keyword can only be used inside an async function (with one exception covered later). It pauses execution of that function until the promise it's given settles, then either returns the fulfilled value or throws the rejection as an error. Crucially, await only pauses the async function itself — the rest of your program keeps running normally, since JavaScript is still single-threaded and non-blocking underneath.

Converting a .then() Chain to async/await

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Error Handling with try/catch

One of the biggest advantages of async/await is that it lets you handle Promise rejections with an ordinary try/catch block, exactly like synchronous errors from the earlier lesson on error handling. If any awaited promise rejects, execution jumps straight to the catch block — no need for a separate .catch() call chained onto every asynchronous step.

try/catch Around await

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Parallel vs. Serial awaits

A subtle but important trap: awaiting one promise at a time inside a loop or sequence of statements runs those operations one after another, even if they don't depend on each other's results. If the operations are independent, start them all first and then await them together with Promise.all() — this runs them in parallel and can be dramatically faster.

Accidentally Serializing Independent Work

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

await Inside Loops

Calling await on a fresh promise inside every iteration of a for loop forces each iteration to wait for the last one to finish, even when the operations have nothing to do with each other. If the iterations are independent, map over the array to start all the promises first, then await Promise.all() on the resulting array — don't await inside the loop itself.

A Brief Note on Top-Level await

Historically, await was only legal inside an async function. Modern JavaScript modules (the ES module system covered in the next lesson) support top-level await, letting you use await directly in a module's top-level code without wrapping it in an async function. This is handy for one-off setup work, like awaiting a database connection before the rest of a module runs — but it's only available inside modules, not in regular scripts.

Top-Level await in a Module

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Mixing Promises and async/await

You don't have to choose one style forever — async/await and raw Promise methods interoperate freely. It's common to write async functions that still use Promise.all(), Promise.race(), or Promise.allSettled() internally, combining the readability of await with the power of the Promise combinators from the previous lesson.

Interview Questions

Quick Quiz

1. What does an async function always return?

2. What happens when you await a rejected promise inside a try block?

3. Why does awaiting three independent promises one at a time run slower than necessary?

4. How do you run several independent async operations in parallel and await all their results?

5. Where is top-level await supported?

6. What does this code log?

async function getValue() {
  return 5;
}

console.log(getValue());

7. In what order does this code log?

async function run() {
  console.log("A");
  await null;
  console.log("B");
}

console.log("start");
run();
console.log("end");