What it does: A function passed to another function to be executed when an async operation completes.
Callback Pattern Structure:
Basic Callback:
function doAsync(data, callback) {
// do something async
setTimeout(() => {
callback("result");
}, 1000);
}
doAsync("input", function(result) {
console.log(result); // runs after 1 second
});Error-First Callback (Node.js style):
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script); // success
script.onerror = () => callback(new Error()); // error
document.head.append(script);
}
loadScript('script.js', function(error, script) {
if (error) {
console.log("Failed to load");
} else {
console.log("Script loaded!");
}
});The Problem - Callback Hell:
// This gets ugly fast
loadScript('1.js', function(error, script) {
if (error) handleError(error);
else {
loadScript('2.js', function(error, script) {
if (error) handleError(error);
else {
loadScript('3.js', function(error, script) {
// pyramid of doom!
});
}
});
}
});Interview Gotchas:
javascript
// 1. Callbacks don't wait
loadScript('script.js');
newFunction(); // ERROR! Script not loaded yet
// 2. Multiple callbacks with same function
function process(callback) {
callback();
callback(); // Might run twice!
}
// 3. Forgetting error handling
doAsync(data, function(result) {
// What if there was an error? 💥
console.log(result.length);
});Bottom line: Callbacks let you handle async operations, but they create nested “pyramid of doom” code that’s hard to read and maintain.
