~/
Async Patterns & Errors
Quiz
...

Async Patterns & Errors

intermediate · updated Tue Sep 22 2026Contribute

Callbacks, promises, async/await, and how to handle failures.

Async Patterns & Errors

Node is built for asynchronous work. Knowing how to write and handle async code — and how to fail gracefully — is essential.

Callbacks (the original)

Early Node used error-first callbacks:

import { readFile } from "node:fs";

readFile("notes.txt", "utf8", (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

The convention: the first argument is an error, the second the result. This works but nests badly — “callback hell”.

Promises

A Promise represents a future value. It is either pending, fulfilled, or rejected:

import { readFile } from "node:fs/promises";

readFile("notes.txt", "utf8")
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

async / await

async/await lets you write async code that reads like synchronous code:

async function load() {
  try {
    const data = await readFile("notes.txt", "utf8");
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

An async function always returns a Promise. await pauses that function only — the rest of the program keeps running.

Running in Parallel

Do not await independent tasks one by one:

// slow — sequential
const a = await getUser(1);
const b = await getUser(2);

// fast — parallel
const [a, b] = await Promise.all([getUser(1), getUser(2)]);

Promise.all rejects as soon as any promise rejects. Promise.allSettled waits for all and reports each result.

Central Error Handling

Do not swallow errors. Either handle them where you can recover, or let them bubble:

async function main() {
  await startServer();
}

main().catch((err) => {
  console.error("Fatal:", err);
  process.exit(1);
});

Async Iteration

Streams and async generators work with for await...of:

for await (const chunk of stream) {
  process.stdout.write(chunk);
}

Best Practices

  1. Prefer async/await — Clearer than raw promises or callbacks.
  2. Await in parallel when independentPromise.all cuts latency.
  3. Never leave a promise unhandled — It can crash the process.
  4. Handle errors at the boundary — Top-level .catch as a last resort.

Common Mistakes

  1. Forgetting await — You get a Promise, not the value.
  2. await inside a loop — Runs sequentially; batch instead.
  3. Swallowing errors — Empty catch blocks hide bugs.
  4. Assuming order — Async callbacks do not run in the order you wrote them.