Callback Hell, also known as "Pyramid of Doom," occurs when multiple nested callbacks make code difficult to read and maintain. It usually happens in asynchronous JavaScript, where functions rely on callbacks to execute sequentially.
function getData(callback) {
setTimeout(() => {
console.log("Fetching data...");
callback(null, "Data received");
}, 1000);
}
function processData(data, callback) {
setTimeout(() => {
console.log("Processing data...");
callback(null, "Data processed");
}, 1000);
}
function saveData(data, callback) {
setTimeout(() => {
console.log("Saving data...");
callback(null, "Data saved");
}, 1000);
}
getData((err, data) => {
if (err) return console.error(err);
processData(data, (err, processedData) => {
if (err) return console.error(err);
saveData(processedData, (err, result) => {
if (err) return console.error(err);
console.log(result);
});
});
});
The nested structure makes it hard to debug and maintain.
Breaking down callbacks into separate named functions improves readability.
function getData(callback) {
setTimeout(() => callback(null, "Data received"), 1000);
}
function processData(data, callback) {
setTimeout(() => callback(null, "Data processed"), 1000);
}
function saveData(data, callback) {
setTimeout(() => callback(null, "Data saved"), 1000);
}
function handleData() {
getData((err, data) => {
if (err) return console.error(err);
processData(data, handleProcessedData);
});
}
function handleProcessedData(err, processedData) {
if (err) return console.error(err);
saveData(processedData, (err, result) => {
if (err) return console.error(err);
console.log(result);
});
}
handleData();
Promises help to write cleaner, chainable asynchronous code.
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve("Data received"), 1000);
});
}
function processData(data) {
return new Promise(resolve => {
setTimeout(() => resolve("Data processed"), 1000);
});
}
function saveData(data) {
return new Promise(resolve => {
setTimeout(() => resolve("Data saved"), 1000);
});
}
getData()
.then(processData)
.then(saveData)
.then(console.log)
.catch(console.error);
Async/Await makes the code even more readable and synchronous-like.
async function handleData() {
try {
const data = await getData();
const processedData = await processData(data);
const result = await saveData(processedData);
console.log(result);
} catch (err) {
console.error(err);
}
}
handleData();
Callback Hell is a common issue in JavaScript but can be avoided by using named functions, Promises, or Async/Await. These techniques make the code cleaner, more readable, and easier to maintain.
Do you have any experiences dealing with Callback Hell? Share your thoughts in the comments below!