Async Patterns
Part of the Backend Developer Roadmap
In short
A backend spends most of its time waiting: for the database, for another API, for a file. Async code lets one Node process serve many users while it waits.
The practical patterns for structuring async backend code cleanly — sequential vs parallel awaits, error propagation, and avoiding callback hell in real route handlers.

Think of it like this
The bank token counter
At the bank you take a token and sit down. You don't stand frozen at the counter; you read a comic. When your number is called, you go up. A Promise is that token: a promise of a result later, while you do other work.
Watch it happen
What happens around an await
Front of the queue is coloured
1/5Request A arrives. The handler calls await db.query(...) and pauses.
The big ideas
What you need to know
A Promise is a future value
It's pending, then either fulfilled with a value or rejected with an error. await waits for it without blocking Node.
await only pauses that function
Other requests keep running. That's why one Node server can handle many users at once.
Run independent work together
await Promise.all([a(), b()]) starts both at once. Two awaits in a row run one after the other.
allSettled for partial results
Promise.allSettled waits for all and tells you which failed, instead of failing everything at the first error.
See it in code
Walk through the code
1// Slow: about 300ms + 200ms, one after another2const user = await getUser(id); // 300ms3const orders = await getOrders(id); // 200ms4 5// Fast: about 300ms, both start together6const [u, o] = await Promise.all([getUser(id), getOrders(id)]);7 8// Keep going even if one source fails9const results = await Promise.allSettled([getWeather(), getNews()]);10for (const r of results) {11 if (r.status === "fulfilled") console.log(r.value);12 else console.error("failed:", r.reason.message);13}1/3Each await waits for the one before it, so total time adds up.
Use sequential awaits only when the second call needs the first one's result. Otherwise, run them together.
Save yourself the pain
Common mistakes
Oops
Calling an async function without await, then using the result as if it were data.
Do this instead
An async function returns a Promise. await it, or the value is a pending Promise, not your data.
Oops
Using await inside a for loop for 100 independent API calls.
Do this instead
Map them to Promises and use Promise.all, maybe in small batches to avoid flooding the other server.
Your mission
Time the difference
Quick check
Promise.all([a(), b(), c()]) where b() rejects. What happens?
Go deeper
- 1Refactor a route handler with multiple sequential awaits into parallel ones where possible.
- 2Write a reusable `asyncHandler` wrapper to catch errors in Express route handlers.
- 3Practice chaining dependent async calls (e.g. create user, then send welcome email) correctly.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.