Async Node.js: Callbacks, Promises, and async/await

Asynchronous code is the heart of Node.js

Almost everything in Node is asynchronous. Understanding the evolution from callbacks to async/await — and how to run work in parallel — is essential to writing correct, fast Node code.

The Three Eras

  • Callbacks — the original style; nesting leads to "callback hell"
  • Promises — chainable objects representing a future value
  • async/await — syntactic sugar that makes promises read like sync code

From Callback to async/await

// Old: nested callbacks
getUser(id, (err, user) => {
  getOrders(user, (err, orders) => { /* ... */ });
});

// Modern: async/await
const user = await getUser(id);
const orders = await getOrders(user);

Promise.all runs independent work concurrently

Sequential vs. Parallel

Awaiting in a loop is sequential and slow when calls are independent. Use Promise.all to run them at once:

Pattern When
await in sequence Each step depends on the previous
Promise.all([...]) Independent calls, want them parallel
Promise.allSettled You need every result, failures included

Common Pitfalls

  1. Forgetting await — you get a Promise, not a value.
  2. Sequential awaits for independent work — wasted time.
  3. Unhandled rejections — always try/catch around awaits.

If two awaits don’t depend on each other, they should probably be a single Promise.all.

What to Learn Next

  • Error handling strategies for async code
  • Streams for processing data incrementally
  • AbortController for cancelling async work

Arivanandhan Chitheshwaran