In short
Before promises, every async Node API took a callback, and plenty still do. You'll meet the (err, data) pattern in old code, libraries and interviews, so learn to read it fluently.
Functions passed as arguments to be invoked once an async operation completes — Node's original async pattern, still seen throughout its core APIs.

Think of it like this
Instructions for the press-wala
You drop shirts at the ironing cart and say: 'When done, hand them to the hostel guard.' That instruction is a callback. He'll first tell you if something went wrong, like a burnt shirt, before handing over the rest. Error first, then the result.
Watch it happen
fs.readFile with a callback
readFile(path, cb)
1/6You hand fs the file path and a function to call later.
The big ideas
What you need to know
A function to call later
A callback is a function you pass to another function, which calls it when the work is done.
Error first
Node callbacks take (err, result). Always check err before touching the result.
Callback hell
Each step nested inside the last builds a sideways pyramid that's hard to read and harder to handle errors in.
Convert to promises
util.promisify wraps a callback API into one that returns a promise, ready for await.
See it in code
Walk through the code
1const fs = require("node:fs");2const { promisify } = require("node:util");3 4fs.readFile("notes.txt", "utf8", (err, text) => {5 if (err) return console.error("Could not read:", err.code); // e.g. ENOENT6 fs.writeFile("copy.txt", text.toUpperCase(), (err2) => {7 if (err2) return console.error(err2);8 console.log("Copied!"); // one level deeper for every step9 });10});11console.log("This line prints first");12 13const readFileAsync = promisify(fs.readFile);14readFileAsync("notes.txt", "utf8").then((t) => console.log(t.length));1/4Error-first: check err and return early, so nothing below runs with bad data.
Save as callbacks.cjs next to a notes.txt file. Today you'd use node:fs/promises directly, but promisify still helps with older libraries that only offer callbacks.
Save yourself the pain
Common mistakes
Oops
Forgetting return after handling the error, so the code carries on with undefined data.
Do this instead
Write if (err) return handle(err); so nothing below that line runs.
Oops
Calling the callback twice, once for an error and again for success.
Do this instead
Every path should call the callback exactly once. Returning early on errors prevents doubles.
Your mission
Escape callback hell
Quick check
In Node's callback style, what does the first argument of the callback hold?
Go deeper
- 1Write a function that accepts a callback and calls it after a `setTimeout`.
- 2Recognize 'callback hell' from deeply nested callback-based code.
- 3Understand why Promises were introduced to solve callback hell.
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.