What is Callback Hell in JavaScript?

What is Callback Hell in JavaScript?

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.

Example of Callback Hell

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.

How to Avoid Callback Hell

1. Using Named Functions

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();

2. Using Promises

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);

3. Using Async/Await

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();

Conclusion

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!