Glossary

Callback

A callback is a function passed as an argument to another function, to be invoked later — either synchronously (like Array.map's callback) or asynchronously (like setTimeout's callback) — when a certain event or condition occurs.

Explanation

Callbacks are JavaScript's original mechanism for handling asynchronous operations and customizable behavior. Array.map(callback) calls your callback once per element — a synchronous callback. fs.readFile(path, callback) calls your callback when the file is read — an asynchronous callback. Both use the same pattern: pass a function, have it called later. The convention for Node.js-style callbacks (error-first callbacks, or "errbacks") is that the first argument is an error (null if none) and subsequent arguments are results: readFile(path, (err, data) => { if (err) throw err; /* use data */ }). This pattern predates Promises and is still common in older Node.js APIs. Callback hell (or "pyramid of doom") occurs when multiple async operations must be sequenced: readFile(path, (err, data) => { parseData(data, (err, parsed) => { saveResult(parsed, (err, result) => { /* 3+ levels deep */ }) }) }). Each level of indentation represents one async step, and error handling must be duplicated at each level. Promises and async/await were invented to solve this. Callbacks remain the right choice for synchronous higher-order functions (map, filter, reduce, sort, forEach), event listeners (addEventListener), and observability hooks (middleware next(), React's setState updater function). The key distinction: synchronous callbacks are executed inline during the current call; asynchronous callbacks are scheduled and run later.

Code Example

javascript
// Synchronous callbacks: common array methods
const nums = [1, 2, 3, 4, 5];
const evens = nums.filter(n => n % 2 === 0);   // [2, 4]
const doubled = nums.map(n => n * 2);           // [2, 4, 6, 8, 10]
const sum = nums.reduce((acc, n) => acc + n, 0); // 15
nums.sort((a, b) => b - a);                      // [5, 4, 3, 2, 1]

// Asynchronous callbacks: old-style Node.js
const fs = require('fs');
fs.readFile('./data.json', 'utf8', (err, data) => {  // error-first
  if (err) return console.error(err);
  const parsed = JSON.parse(data);
  console.log(parsed);
});

// Callback hell (avoid this)
readUser(id, (err, user) => {
  if (err) return handleError(err);
  readPosts(user.id, (err, posts) => {
    if (err) return handleError(err);
    readComments(posts[0].id, (err, comments) => {
      // 3+ levels deep, error handling repeated
    });
  });
});

// Promise equivalent (flat, single error handler)
readUser(id)
  .then(user => readPosts(user.id))
  .then(posts => readComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(handleError);

Why It Matters for Engineers

Callbacks are foundational JavaScript. Every event listener you write, every Array.map() you use, every middleware next() you call — these are callbacks. Understanding the synchronous vs asynchronous distinction prevents bugs where you expect a callback's result to be available immediately when it's actually executed later. Understanding callbacks also helps you understand why Promises and async/await were invented: they solve callback hell's readability problem without replacing the underlying callback mechanism (Promises still use callbacks internally).

Related Terms

Promise · Async/Await · Event Loop · Closure

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Programming Foundations → →