Compile Ready
Module 8 · Asynchronous JavaScript

async / await

Intermediate13m read18m practice31m total
async awaitPromisesSequentialParallelError Handling

Introduction

async/await is syntax built on promises. An async function always returns a promise, and await pauses that async function until the awaited value is fulfilled or rejected.

It makes promise-based code read like ordinary sequential code, but it does not make asynchronous work synchronous. The call stack still unwinds, and the continuation after await runs later as promise/microtask work.

Why This Matters

Modern frontend and backend JavaScript is full of async functions. Interviews test whether you know the difference between clean syntax and actual concurrency: sequential awaits can be slow, while starting promises first and then awaiting Promise.all enables parallel work.

Theory

What async does

An async function wraps its return value in a promise. Returning 42 from an async function fulfills the returned promise with 42. Throwing an error rejects the returned promise.

What await does

await expression converts the expression to a promise-like value and suspends only the current async function. The outer caller keeps running. When the awaited value settles, the rest of the async function continues later. That continuation is scheduled through promise mechanics, so it behaves like microtask work.

Error handling

Inside an async function, use try/catch around awaited work. A rejection from an awaited promise acts like a thrown error at that line. If it is not caught, the async function's returned promise rejects.

Sequential versus parallel awaits

This is one of the most important production and interview distinctions:

  • Sequential: const a = await loadA(); const b = await loadB(); starts loadB only after loadA finishes. Use this when B depends on A.
  • Parallel: const aPromise = loadA(); const bPromise = loadB(); const [a, b] = await Promise.all([aPromise, bPromise]); starts both immediately and waits for both. Use this when they are independent.

Top-level await note

Top-level await exists in ES modules, but many interview snippets avoid it because support depends on module context. In ordinary scripts, use an async function wrapper.

Common mistakes

Do not use await inside Array.prototype.forEach expecting the outer function to wait. Use for...of for sequential work or Promise.all(items.map(...)) for parallel work.

Visual Diagrams

Async function execution

caller invokes async function
        |
        v
sync part of async function runs immediately
        |
        v
await reached
        |
        +---- async function returns pending promise to caller
        |
        v
awaited promise settles later
        |
        v
continuation after await runs as promise work

`await` pauses the async function, not the whole program.

Sequential versus parallel

Sequential:
  start A -> wait A -> start B -> wait B

Parallel:
  start A ------------------ wait both
  start B ------------------ wait both

Parallel awaits require starting the promises before awaiting their results.

Code Examples

Sequential when one result depends on another

This is correct when the second request needs the first result.

Loading…

Parallel when operations are independent

Start both promises before awaiting. This often cuts total wait time.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1
2async function demo() {
3 console.log('inside 1');
4 await Promise.resolve();
5 console.log('inside 2');
6}
7
8console.log('before');
9demo();
10console.log('after');

Predict the output #2

javascript
1
2function wait(label) {
3 return new Promise(function (resolve) {
4 setTimeout(function () {
5 console.log(label);
6 resolve(label);
7 }, 0);
8 });
9}
10
11async function run() {
12 const a = wait('A');
13 const b = wait('B');
14
15 await a;
16 await b;
17
18 console.log('done');
19}
20
21run();
22console.log('scheduled');

Coding Exercises

Convert sequential awaits to parallel awaits

Medium

Implement loadPair(loadA, loadB) so it starts both independent async functions immediately, waits for both, and returns an object { a, b }.

Interview Questions

1What does an async function return?

It always returns a promise. A returned plain value fulfills that promise, and a thrown error rejects it. If it returns another promise, the async function's returned promise adopts that result.

Asked at:GoogleMetaAmazon
2How do you decide between sequential and parallel awaits?

Use sequential awaits when later work depends on earlier results. Use parallel awaits when operations are independent: start all promises first, then await Promise.all. This improves latency without changing the single-threaded execution model.

Follow-ups

  • What happens if one promise in `Promise.all` rejects?
  • Why is `await` inside `forEach` usually a bug?

Quiz

1. What happens when an `async` function throws?

2. Which pattern starts two independent operations in parallel?

Summary

  • `async` functions always return promises.
  • `await` pauses only the current async function; callers continue running.
  • Awaited rejections behave like thrown errors and can be handled with `try/catch`.
  • Start independent promises before awaiting to run them in parallel with `Promise.all`.

Cheat Sheet

async: return value → fulfilled promise; thrown error → rejected promise.

await: pauses the async function, not the whole program.

Sequential: await A, then start B when B depends on A.

Parallel: start A and B first, then await Promise.all([a, b]).

Pitfall: forEach(async item => ...) does not make the outer function wait.