async / await
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();startsloadBonly afterloadAfinishes. 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
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: 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.
Parallel when operations are independent
Start both promises before awaiting. This often cuts total wait time.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
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
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
MediumImplement 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.
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.