Async Patterns (callbacks → promises → async/await)

Asynchronous code lets Node start slow work — files, networks, timers — and keep running instead of freezing, then handle the result later with callbacks, promises, or async/await.

Learn Async Patterns (callbacks → promises → async/await) in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a…

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

By the end of this lesson you'll trace how a callback runs later, see why nested callbacks become "callback hell," rewrite the same logic with promises, and finally with clean async/await using try/catch.

What You'll Learn in This Lesson

1️⃣ Callbacks: "Call Me Back When You're Done"

The oldest async pattern in Node is the callback : you pass a function into a slow operation, and Node runs that function once the work finishes. Because Node is non-blocking, the rest of your script keeps running in the meantime. Read the comments and predict the print order before you check.

Callbacks work well for one step. The trouble starts when each async step depends on the previous one — the code nests sideways into a staircase known as callback hell .

2️⃣ Promises: A Value That Isn't Ready Yet

A promise is an object that stands in for a result that will arrive later. It either resolves (success) or rejects (failure). You attach .then() for success and .catch() for failure — and chaining .then() keeps sequential steps flat instead of nested.

3️⃣ async/await: Async Code That Reads Top-to-Bottom

async/await is syntax built on promises. Mark a function async , then use await to pause until a promise resolves and hand you its value — no .then() , no nesting. Errors are caught with ordinary try/catch . Remember: await pauses only its own function, never the whole program.

Notice that "main() started" prints first : calling an async function returns immediately, and the awaited work resumes later. That's the non-blocking event loop at work.

Your turn. Finish the async function below by replacing each marked spot. Follow the // TODO comments, then run it and compare with the expected output.

No blanks this time — just a brief and an outline. Use Promise.all to await two promises at once, then combine the results into one line. Run it with node parallel.js and check your output against the example.

📋 Quick Reference — Async Patterns

Practice quiz

What is a callback in Node.js?

  • A way to pause the whole program
  • A special kind of variable
  • A function passed to another function to run later when work finishes
  • A built-in Node module

Answer: A function passed to another function to run later when work finishes. A callback is a function you hand to an async operation so it can call you back once the slow work completes.

In the callback example, what order do the three messages print?

  • 1, 2, 3
  • 1, 3, 2
  • 3, 2, 1
  • 2, 1, 3

Answer: 1, 2, 3. Because Node is non-blocking, the synchronous line 2 runs before the setTimeout callback (line 3).

What term describes deeply nested callbacks that grow sideways?

  • Promise chaining
  • Event looping
  • Async sprawl
  • Callback hell

Answer: Callback hell. When each async step nests inside the previous callback, the staircase shape is called callback hell.

What does a Promise represent?

  • A synchronous return value
  • A value that is not ready yet and will resolve or reject later
  • A timer that always fires
  • A network socket

Answer: A value that is not ready yet and will resolve or reject later. A Promise stands in for a future result that eventually resolves (success) or rejects (failure).

Which method handles a successful promise value?

  • .then()
  • .catch()
  • .finally()
  • .reject()

Answer: .then(). .then() runs on success; .catch() runs on failure.

What keyword lets you use await inside a function?

  • defer
  • yield
  • async
  • wait

Answer: async. Marking a function async lets you use await inside it; await is only valid in async functions.

Does await block the entire program while it waits?

  • Yes, the whole process freezes
  • No, it only pauses its own async function
  • Only on Windows
  • Only for network calls

Answer: No, it only pauses its own async function. await pauses just the async function it is in; the event loop keeps running everything else.

What does every async function return?

  • undefined always
  • A callback
  • A Buffer
  • A promise

Answer: A promise. An async function always returns a promise, so callers can await it too.

When should you use Promise.all?

  • To run promises strictly one after another
  • To run independent async operations in parallel and await them together
  • To cancel a promise
  • To convert a callback to a promise

Answer: To run independent async operations in parallel and await them together. Promise.all starts independent operations together, so total time is the slowest one, not the sum.

How do you catch a rejected promise when using await?

  • Add a second argument to await
  • Use .then() only
  • Wrap the await in try/catch
  • It cannot be caught

Answer: Wrap the await in try/catch. try/catch around an await handles a rejected promise just like a thrown error.