JavaScript Callbacks
A callback is a function passed as an argument to another function. The receiving function can then call (invoke) the callback at some point during its execution. Callbacks are the foundation of asynchronous programming in JavaScript.
What Are Callbacks?
A callback function is simply a function that is passed to another function as a parameter and is invoked inside that function. Callbacks allow you to control when a function runs.
function greet(name, callback) {
console.log('Hello, ' + name + '!');
callback();
}
function sayGoodbye() {
console.log('Goodbye!');
}
greet('Alice', sayGoodbye);Callback Functions in Practice
Callbacks are used extensively in JavaScript. Array methods like forEach, map, and filter all use callbacks. Event listeners also use callbacks.
const numbers = [1, 2, 3, 4, 5];
// forEach with callback
numbers.forEach(function(num) {
console.log('Number: ' + num);
});
// map with callback
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log('Doubled: ' + doubled);
// filter with callback
const evens = numbers.filter(function(num) {
return num % 2 === 0;
});
console.log('Evens: ' + evens);setTimeout and setInterval
setTimeout() calls a callback function once after a specified delay. setInterval() calls a callback function repeatedly at specified intervals.
// setTimeout - runs once after delay
console.log('Before timeout');
setTimeout(function() {
console.log('This runs after 1 second');
}, 1000);
// setInterval example (conceptual)
let count = 0;
const intervalId = setInterval(function() {
count++;
console.log('Tick: ' + count);
if (count >= 3) {
clearInterval(intervalId);
console.log('Interval cleared');
}
}, 500);Callback Hell
When callbacks are nested inside callbacks, the code becomes deeply indented and hard to read. This pattern is known as 'callback hell' or the 'pyramid of doom'.
// Simulating callback hell
function step1(callback) {
setTimeout(function() {
console.log('Step 1 complete');
callback();
}, 100);
}
function step2(callback) {
setTimeout(function() {
console.log('Step 2 complete');
callback();
}, 100);
}
function step3(callback) {
setTimeout(function() {
console.log('Step 3 complete');
callback();
}, 100);
}
// Nested callbacks (pyramid of doom)
step1(function() {
step2(function() {
step3(function() {
console.log('All steps done!');
});
});
});Error-First Callbacks
A common pattern in Node.js is the error-first callback, where the first argument of the callback is reserved for an error object. If no error occurred, the first argument is null.
function fetchData(id, callback) {
if (id <= 0) {
callback(new Error('Invalid ID'), null);
return;
}
// Simulate successful data fetch
const data = { id: id, name: 'Item ' + id };
callback(null, data);
}
// Using error-first callback
fetchData(1, function(err, data) {
if (err) {
console.log('Error: ' + err.message);
return;
}
console.log('Success: ' + JSON.stringify(data));
});
fetchData(-1, function(err, data) {
if (err) {
console.log('Error: ' + err.message);
return;
}
console.log('Success: ' + JSON.stringify(data));
});| Problem with Callbacks | Description |
|---|---|
| Callback Hell | Deeply nested callbacks are hard to read |
| Inversion of Control | You trust another function to call your callback |
| Error Handling | No standard way (error-first is a convention) |
| No return value | Callbacks don't return values to the caller |
| Hard to compose | Difficult to combine multiple async operations |