async/await
Write async code that reads top-to-bottom using async functions and await — with proper error handling and parallel patterns.
async/await is syntactic sugar over promises — it does not replace the event loop or make code magically synchronous. async marks a function as always returning a Promise; await pauses that function until a Promise settles, then unwraps the value or throws on rejection. The readability win is huge: sequential steps look like synchronous code while still non-blocking.
async functions always return a Promise
Writing async function foo() {} is like function foo() { return Promise.resolve(...) } for return values. Thrown errors become rejected promises. Callers must await foo() or use .then — ignoring the returned promise causes floating async work and missed errors.
42
boom
awaited: 42await — pause without blocking the thread
await suspends only the current async function, not the entire program. Other requests, timers, and UI events still process. When the promise fulfills, execution resumes after the await line with the unwrapped value. If it rejects, await throws — catch it with try/catch like synchronous exceptions.
start
sync after call
A
B
endtry/catch with await
Rejected promises from await jump to catch — the same mental model as synchronous throw. finally runs cleanup whether success or failure. For HTTP handlers, one try/catch around the handler body with mapped error responses beats scattered .catch on every line.
Success: User 1
Request finished
Handler error: Invalid id
Request finishedSequential vs parallel await
Awaiting inside a loop runs requests one after another — total time sums. Starting promises first then await Promise.all([...]) runs independent I/O in parallel — total time is roughly the slowest request. Use sequential when step B needs step A's result; use parallel when calls are independent.
async/await is not parallel by default: two await statements in sequence wait for each other. Use Promise.all when tasks are independent. Keep try/catch close to the await that can fail so error messages retain context instead of bubbling through unrelated layers.
- 1async functions return promises automatically
- 2await unwraps fulfillment; rejection throws to try/catch
- 3Parallelize independent work with Promise.all
- 4Never use forEach with async callbacks for flow control
- 5Next lesson: Promise.all, race, allSettled, any