Skip to main content
Practice Problems

callback functions and callback hell in JavaScript

What is a Callback Function?

A callback is a function that is passed as an argument to another function and is called later (called back) when a certain task completes or condition is met.


Basic Example

javascript
function greet(name, callback) { const message = `Hello, ${name}!`; callback(message); } greet("Alice", (msg) => console.log(msg)); // "Hello, Alice!"

Synchronous Callbacks

Used in array methods and iteration:

javascript
const numbers = [1, 2, 3, 4, 5]; // forEach callback numbers.forEach((num) => console.log(num)); // map callback const doubled = numbers.map((num) => num * 2); // filter callback const evens = numbers.filter((num) => num % 2 === 0); // sort callback const sorted = numbers.sort((a, b) => a - b);

Asynchronous Callbacks

Used for operations that take time:

javascript
// setTimeout setTimeout(() => { console.log("Executed after 1 second"); }, 1000); // Event listeners button.addEventListener("click", (event) => { console.log("Button clicked!", event.target); }); // Reading files (Node.js) fs.readFile("data.txt", "utf8", (error, data) => { if (error) { console.error("Error:", error); return; } console.log(data); });

Error-first Callback Pattern

A Node.js convention — the first parameter is always an error:

javascript
function fetchData(url, callback) { // callback(error, data) try { const data = /* ... fetch data ... */; callback(null, data); // Success: error is null } catch (error) { callback(error, null); // Failure: pass error } } fetchData("/api/users", (error, data) => { if (error) { console.error("Failed:", error); return; } console.log("Data:", data); });

Callback Hell (Pyramid of Doom)

When multiple async operations depend on each other, callbacks nest deeply:

javascript
// ❌ Callback Hell getUser(userId, (err, user) => { if (err) return handleError(err); getOrders(user.id, (err, orders) => { if (err) return handleError(err); getOrderDetails(orders[0].id, (err, details) => { if (err) return handleError(err); getShippingInfo(details.shipId, (err, shipping) => { if (err) return handleError(err); console.log(shipping); }); }); }); });

Solutions to Callback Hell

1. Promises

javascript
getUser(userId) .then(user => getOrders(user.id)) .then(orders => getOrderDetails(orders[0].id)) .then(details => getShippingInfo(details.shipId)) .then(shipping => console.log(shipping)) .catch(handleError);

2. async/await

javascript
async function getShipping(userId) { const user = await getUser(userId); const orders = await getOrders(user.id); const details = await getOrderDetails(orders[0].id); const shipping = await getShippingInfo(details.shipId); return shipping; }

3. Named Functions

javascript
function handleUser(err, user) { if (err) return handleError(err); getOrders(user.id, handleOrders); } function handleOrders(err, orders) { if (err) return handleError(err); getOrderDetails(orders[0].id, handleDetails); }

Important:

Callbacks are the foundation of asynchronous JavaScript. However, for complex async flows, prefer Promises or async/await over deeply nested callbacks. Array methods like map, filter, reduce use synchronous callbacks and are perfectly fine.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems