Skip to main content

Passing a callback to another function

To pass a callback, name the function as an ordinary argument when you call another function, and inside the receiving function call it by its parameter name. This works because functions in JavaScript are values (first-class citizens): they can be assigned to variables, stored in arrays and passed as arguments.

Theory

TL;DR

  • A callback is passed as an ordinary argument: doSomething(afterDone).
  • Pass a reference to the function, with no parentheses. Parentheses mean a call and pass its result, often undefined.
  • Inside the receiving function the callback is called by its parameter name: callback().
  • You can pass a named function, an anonymous function, or an arrow function written inline at the call site.
  • The receiving function does not care what the callback does: it receives that behaviour from outside.

Quick example

javascript
function doSomething(callback) { console.log('Doing something...'); callback(); // call the callback we were given } function afterDone() { console.log('Done!'); } doSomething(afterDone);

Output:

text
Doing something... Done!

Three ways to pass a callback

The notation differs, the idea is the same: a function ends up in the callback parameter.

javascript
// 1. A named function, we pass a reference to it function afterDone() { console.log('Done!'); } doSomething(afterDone); // 2. An anonymous function right in the argument doSomething(function () { console.log('Done!'); }); // 3. An arrow function, the shortest form doSomething(() => { console.log('Done!'); });
FormWhen it is convenient
Named functionThe logic is reused in several places and is easy to test on its own
Anonymous functionYou need the callback to have its own this or arguments
Arrow functionA short one-off handler, this is taken from the surrounding scope

Passing arguments into the callback

The receiving function decides which arguments the callback is called with. The caller only describes what to do with those values.

javascript
function doSomething(callback) { const result = 42; callback(result); // pass data into the callback } doSomething(value => { console.log('Got', value); // Got 42 });

If you need to fix your own arguments up front, wrap the call in an arrow function or use bind.

javascript
function greet(name) { console.log('Hello, ' + name); } doSomething(() => greet('Maria')); // wrapper keeps the argument doSomething(greet.bind(null, 'Oleh')); // the same via bind

Calling instead of passing: the main trap

afterDone is a function, afterDone() is already its result. Parentheses in the argument run the function immediately, so the receiving function gets a value instead of behaviour.

javascript
doSomething(afterDone); // correct: pass the function itself doSomething(afterDone()); // wrong: runs now and passes undefined

The same principle holds for any API that takes a callback: setTimeout(fn, 100), addEventListener('click', fn), array.map(fn).

Guarding against a missing callback

If the parameter is optional, check it before calling or give it a default value, otherwise you get TypeError: callback is not a function.

javascript
function doSomething(callback = () => {}) { console.log('Doing something...'); callback(); } doSomething(); // works even without an argument

Common mistakes

  • Passing afterDone() instead of afterDone: the function runs immediately and the parameter receives undefined.
  • Writing parentheses in setTimeout(sayHi(), 100) and then wondering why the timer does nothing.
  • Calling the callback without checking that one was actually passed, and getting a TypeError.
  • Passing an object method directly, obj.handler, and losing this. An arrow wrapper or bind fixes it.
  • Expecting a value from an asynchronous callback to be available right after the function call: it only appears once the callback actually runs.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.