In short
A backend spends most of its life waiting: for the database, for other APIs, for the disk. Async programming lets you wait for many things at once instead of one after another.
The general practice of writing code that doesn't block while waiting for slow operations (I/O, network, timers) — essential to Node's entire performance model.

Think of it like this
Giving clothes to the dhobi
You don't stand at the dhobi's while he washes. You drop the clothes, go to class, and collect them later. Even better, drop the clothes, order books and book a cycle repair together, then pick everything up in the evening. Start, move on, collect: that's async.
Watch it happen
Building a dashboard response
GET /dashboard
1/6The response needs two things: the user's data and today's weather.
The big ideas
What you need to know
Sync waits, async doesn't
Synchronous code stops until each step finishes. Async code starts a task and gets notified when it's done.
Three styles, one idea
Callbacks came first, then promises, then async/await. Under the hood, modern code is promises all the way.
Parallel when independent
If two calls don't need each other's results, start both and wait for both with Promise.all.
Errors still need a home
A rejected promise with no handler crashes modern Node. Catch errors where you await.
See it in code
Walk through the code
1const wait = (ms, value) => new Promise((r) => setTimeout(() => r(value), ms));2const getUser = () => wait(120, { name: "Asha" });3const getWeather = () => wait(200, { temp: 31 });4 5console.time("one by one");6const u1 = await getUser();7const w1 = await getWeather();8console.timeEnd("one by one"); // about 320 ms9 10console.time("together");11const [u2, w2] = await Promise.all([getUser(), getWeather()]);12console.timeEnd("together"); // about 200 ms13 14console.log(u2.name, w2.temp);1/3Two fake slow calls, standing in for a database query and an API request.
Save as async.mjs and run it with node. Use Promise.all only when the calls don't depend on each other's results.
Save yourself the pain
Common mistakes
Oops
Awaiting independent calls one after another, especially inside a loop.
Do this instead
Start them together and await Promise.all. Await in sequence only when a step needs the previous result.
Oops
Starting async work and never handling its error.
Do this instead
An unhandled promise rejection crashes modern Node. Await it inside try/catch, or attach .catch().
Your mission
Speed up a slow handler
Quick check
getUser takes 100 ms and getOrders takes 300 ms, and neither needs the other. How long does await Promise.all([getUser(), getOrders()]) take?
Go deeper
- 1Identify 3 asynchronous operations in a typical backend request (DB query, API call, file read).
- 2Compare how the same async task looks with callbacks, Promises, and async/await.
- 3Learn common async pitfalls: unhandled rejections, forgetting to await.
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.