Promises vs callbacks

JavaScript

The short answer

Both promises and callbacks are ways to handle asynchronous operations in JavaScript. Callbacks came first and are simpler, but they get messy with multiple async operations. Promises were introduced to fix those problems — they give you a cleaner way to chain operations and handle errors.

How callbacks work

A callback is just a function you pass to another function. The idea is simple — "when you are done with your work, call this function."

function fetchUser(userId, callback) {
setTimeout(() => {
const user = { id: userId, name: 'John' };
callback(user);
}, 1000);
}
fetchUser(1, (user) => {
console.log(user.name); // "John"
});

This works fine for one operation. But what happens when you need to do multiple async operations one after another?

fetchUser(1, (user) => {
fetchOrders(user.id, (orders) => {
fetchOrderDetails(orders[0].id, (details) => {
fetchShipping(details.shippingId, (shipping) => {
console.log(shipping);
});
});
});
});

This is called callback hell. The code keeps nesting deeper and deeper, making it very hard to read and maintain.

How promises work

A promise is an object that represents a value that will be available in the future. Instead of passing a callback, the function returns a promise, and you use .then() to handle the result.

function fetchUser(userId) {
return new Promise((resolve) => {
setTimeout(() => {
const user = { id: userId, name: 'John' };
resolve(user);
}, 1000);
});
}
fetchUser(1).then((user) => {
console.log(user.name); // "John"
});

The same nested example from above becomes flat with promises:

fetchUser(1)
.then((user) => fetchOrders(user.id))
.then((orders) => fetchOrderDetails(orders[0].id))
.then((details) => fetchShipping(details.shippingId))
.then((shipping) => console.log(shipping));

No nesting. Each .then() returns a new promise, so you can chain them one after another.

Pros and cons

Callbacks:

Pros:

  • Simple to understand for basic cases
  • No extra syntax or concepts needed
  • They work everywhere, even in older JavaScript

Cons:

  • Callback hell — deep nesting with multiple async operations
  • Error handling is messy — you have to pass errors manually in every callback (like Node.js (error, result) pattern)
  • Hard to run multiple async operations in parallel
  • No built-in way to cancel

Promises:

Pros:

  • Flat chaining with .then() — no nesting
  • Built-in error handling with .catch() — one .catch() handles errors from the entire chain
  • Promise.all() to run multiple operations in parallel
  • Promise.race() to use the first result that comes back
  • Works with async/await for even cleaner code

Cons:

  • Slightly more complex to create (you need to understand resolve and reject)
  • Once a promise starts, you cannot cancel it (without an AbortController)
  • Debugging can be harder — stack traces with promises are sometimes less clear than with callbacks

Error handling — the biggest difference

With callbacks, you have to handle errors in every single callback:

fetchUser(1, (error, user) => {
if (error) {
console.log('Error fetching user');
return;
}
fetchOrders(user.id, (error, orders) => {
if (error) {
console.log('Error fetching orders');
return;
}
// and so on...
});
});

With promises, one .catch() at the end handles everything:

fetchUser(1)
.then((user) => fetchOrders(user.id))
.then((orders) => fetchOrderDetails(orders[0].id))
.catch((error) => {
console.log('Something went wrong:', error);
});

If any step fails, the .catch() at the end picks it up. You do not have to check for errors at every step.

Interview Tip

When answering this question, show both patterns with the same example so the interviewer can see the difference clearly. Start with callbacks, show callback hell, then show how promises flatten the code. Mention that async/await is built on promises and makes the code even cleaner. The key thing interviewers want to hear is that you understand error handling is the biggest advantage of promises over callbacks.

Why interviewers ask this

This question tests if you understand the evolution of asynchronous JavaScript. Interviewers want to see if you know why promises were introduced, what problems they solve, and when callbacks might still make sense. A candidate who can compare both approaches with clear examples and explain the tradeoffs shows they have a good understanding of JavaScript fundamentals.