Asynchronous Callbacks
Introduction
A callback is a function passed to another function to be called later. An asynchronous callback is invoked after some future event: a timer fires, data arrives, a user clicks, or an operation completes.
Callbacks are the oldest JavaScript async pattern. They are still everywhere, even when hidden behind promises and async/await.
Why This Matters
Callbacks reveal two interview-critical ideas: not every callback is asynchronous, and passing a callback creates inversion of control. You give another function the power to decide if, when, how often, and with what arguments your function runs.
Theory
Callback does not automatically mean async
[1, 2, 3].map(callback) calls the callback synchronously during the same stack. setTimeout(callback, 0) calls the callback asynchronously as a later task. Always ask: who invokes the callback, and when?
One-shot versus repeated callbacks
Some async callbacks are one-shot, such as a timer or a single completion handler. Others are repeated, such as event listeners or intervals. Repeated callbacks need cleanup, otherwise they can cause leaks or duplicate work.
Error-first convention
Many Node-style APIs use (error, data) callbacks. The first argument is non-null if the operation failed. This convention made errors explicit before promises were standardized, but it also created repetitive branching and nested control flow.
Inversion of control risks
When you hand a callback to another function, you trust that function to call it exactly once, call it asynchronously if promised, preserve arguments, and handle errors. Bugs such as double-calling a callback are common in callback-heavy systems.
Callback scheduling
A callback-based API can still choose different scheduling mechanisms: synchronous invocation, microtask scheduling, or task scheduling. Good APIs document this. In interviews, always trace the specific scheduling primitive in the snippet.
Visual Diagrams
Your code | | passes callback v Async API or helper | | later decides when to call v callback(value)
Callbacks invert control: the callee owns the moment of invocation.
Synchronous:
caller -> helper -> callback -> helper returns -> caller continues
Asynchronous:
caller -> helper schedules work -> helper returns -> caller continues
later task -> callback
The word callback describes a shape, not a scheduling guarantee.
Code Examples
Error-first callback style
This is common in Node-style APIs. The pattern is useful to recognize even if modern code wraps it in promises.
Guard against double callbacks
Robust callback APIs often protect consumers from accidental double invocation.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1 2function maybeAsync(callback) {3 callback('sync');4 5 setTimeout(function () {6 callback('async');7 }, 0);8}9 10console.log('before');11 12maybeAsync(function (value) {13 console.log(value);14});15 16console.log('after');Predict the output #2
1 2console.log('start');3 4setTimeout(function () {5 console.log('callback');6}, 0);7 8Promise.resolve().then(function () {9 console.log('promise');10});11 12console.log('end');Coding Exercises
Create a once-only async callback
MediumImplement onceAsync(callback) so the returned function can be called many times, but schedules the original callback at most once. The original callback should run asynchronously with the first value.
Interview Questions
1Does passing a callback make code asynchronous?
No. A callback is just a function argument. It is asynchronous only if the receiving API invokes it later, such as from a timer, event, or I/O completion. Array methods like map call callbacks synchronously.
2What is inversion of control in callback-based code?
You hand control of your continuation to another function. That function decides when to invoke it, whether to invoke it once or multiple times, what arguments to pass, and how errors are represented. Promises reduce some of this risk by standardizing one settlement and chainable error propagation.
Follow-ups
- How do promises prevent double completion?
- Why are repeated event callbacks different from completion callbacks?
Quiz
1. Which callback is synchronous?
2. What is a common risk of callback-based APIs?
Summary
- A callback is a function passed to be invoked by another function.
- Callbacks can be synchronous or asynchronous depending on the API.
- Async callbacks often run as future tasks, but callback style alone does not determine the queue.
- Callback-heavy code suffers from inversion of control and repetitive error handling.
Cheat Sheet
Callback: function passed to another function.
Not always async: map is sync; setTimeout is async.
One-shot: completion handlers, timers.
Repeated: events, intervals.
Risk: inversion of control, double calls, missing errors, hard nesting.