callback
A callback is a function passed as an argument to another function, which then calls it at a point of its own choosing.
Functions are values in JavaScript, so one function can receive another as an argument the same way it receives a number. The receiving function also decides how many times to call the callback and with which arguments. Callbacks are often written as arrow functions, and they commonly form a closure over variables from the code that created them.
A synchronous callback runs before the receiving function returns. Array methods such as .map() and .forEach() call theirs once per element. An asynchronous callback runs later, after the current code has finished. Timers and the handlers registered with Promise methods such as .then() work this way.
Passing a method as a callback hands over the function alone, so the call loses its this binding. The callback-based Node.js APIs use error-first callbacks, whose first argument is an Error object or null. Deeply nested asynchronous callbacks are known as callback hell, which promises and async/await flatten.
Example
Say a checkout script totals a cart with .forEach() and then schedules a save with setTimeout(), a timer function that browsers and Node.js provide rather than ECMAScript:
checkout.mjs
const cart = [8, 20, 5];
let total = 0;
cart.forEach((price) => {
total += price;
});
console.log(`Total: ${total}`);
let saved = false;
setTimeout(() => {
saved = true;
console.log("Save callback ran");
}, 0);
console.log(`Saved yet: ${saved}`);
Running the file shows when each callback runs:
$ node checkout.mjs
Total: 33
Saved yet: false
Save callback ran
The .forEach() callback runs once per price before the next line, so the total is ready. The timer callback waits until the rest of the program has finished, so the check right after setTimeout() still sees false.
The receiving function picks the arguments, not the callback’s author. A form that sends quantities as strings shows the gotcha:
> ["7", "8", "9"].map(parseInt)
[ 7, NaN, NaN ]
> ["7", "8", "9"].map((text) => parseInt(text, 10))
[ 7, 8, 9 ]
The .map() method calls its callback with each element, its index, and the array. parseInt() reads the index as a radix. Index 0 counts as no radix, so "7" is parsed in base 10. Radix 1 isn’t a valid base, so "8" gives NaN, and base 2 has no digit 9, so "9" gives NaN too. The arrow function passes only the arguments parseInt() expects.
Related Resources
Have a question about this? Mentor AI can show you examples, compare related terms, and point you to tutorials.
By Leodanis Pozo Ramos • Updated Nov. 5, 2026