DEV Community

HARSHITH GADDAM
HARSHITH GADDAM

Posted on

My JavaScript Learning Journey: Call Back Hell & Promises

Today I learned one of the most important concepts in JavaScript—asynchronous programming with Promises. Initially, Promises seemed confusing, but after understanding how they work internally, the concept became much clearer.

1. What is Callback Hell?

A callback is a function passed to another function that executes after an asynchronous operation completes.

When multiple asynchronous operations depend on each other, callbacks become deeply nested.

getUser(userId, function(user) {
    getOrders(user.id, function(orders) {
        getOrderDetails(orders[0].id, function(details) {
            processOrder(details, function(result) {
                console.log(result);
            });
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

This deeply nested structure is called Callback Hell or the Pyramid of Doom.

Problems with Callback Hell

  • Difficult to read
  • Hard to debug
  • Difficult to maintain
  • Error handling becomes complicated

To solve these problems, JavaScript introduced Promises.


2. What is a Promise?

A Promise is an object that represents the eventual result of an asynchronous operation.

It promises that it will either:

  • Complete successfully
  • Fail with an error

A Promise starts in the Pending state and eventually becomes either Fulfilled or Rejected.


3. Promise States

Every Promise has three possible states.

Pending

The asynchronous operation is still running.

Fulfilled

The operation completed successfully.

Rejected

The operation failed.

A Promise can change its state only once.

Pending
   |
   | resolve()
   ▼
Fulfilled

OR

Pending
   |
   | reject()
   ▼
Rejected
Enter fullscreen mode Exit fullscreen mode

4. Creating a Promise

A Promise is created using the Promise constructor.

const promise = new Promise((resolve, reject) => {

});
Enter fullscreen mode Exit fullscreen mode

One important thing I learned today is that I do not create resolve and reject myself.

JavaScript automatically provides these two functions to the Promise executor.

Conceptually, it works like this:

new Promise((resolve, reject) => {

});
Enter fullscreen mode Exit fullscreen mode

Here,

  • resolve() is used when the operation succeeds.
  • reject() is used when the operation fails.

5. Using resolve() and reject()

Example:

const promise = new Promise((resolve, reject) => {

    const marks = 80;

    if (marks >= 35)
        resolve("Pass");
    else
        reject("Fail");

});
Enter fullscreen mode Exit fullscreen mode

If the condition is true, the Promise becomes Fulfilled.

Otherwise, it becomes Rejected.


6. Handling Promises

then()

.then() executes only when the Promise is fulfilled.

promise.then(result => {
    console.log(result);
});
Enter fullscreen mode Exit fullscreen mode

catch()

.catch() executes only when the Promise is rejected.

promise.catch(error => {
    console.log(error);
});
Enter fullscreen mode Exit fullscreen mode

finally()

.finally() executes whether the Promise succeeds or fails.

It is useful for cleanup operations like hiding loading indicators or closing connections.

promise.finally(() => {
    console.log("Finished");
});
Enter fullscreen mode Exit fullscreen mode

7. Promise Chaining

Every .then(), .catch(), and .finally() returns another Promise.

This allows us to chain multiple asynchronous operations.

Promise.resolve(10)
    .then(value => value * 2)
    .then(value => value + 5)
    .then(value => console.log(value));
Enter fullscreen mode Exit fullscreen mode

Output:

25
Enter fullscreen mode Exit fullscreen mode

Each .then() receives the value returned by the previous .then().


8. Error Propagation

One of the most interesting concepts I learned today is Error Propagation.

If an error occurs inside any .then() callback, JavaScript automatically skips all remaining .then() callbacks and jumps directly to the nearest .catch().

Promise.resolve(10)
    .then(value => {
        throw new Error("Something went wrong");
    })
    .then(() => {
        console.log("This will never execute");
    })
    .catch(error => {
        console.log(error.message);
    });
Enter fullscreen mode Exit fullscreen mode

Output:

Something went wrong
Enter fullscreen mode Exit fullscreen mode

This makes error handling much simpler because a single .catch() can handle errors from multiple Promise operations.


9. Practical Promise Example

function login(username, password) {

    return new Promise((resolve, reject) => {

        setTimeout(() => {

            if (username === "harshith" && password === "1234") {
                resolve("Login Successful");
            } else {
                reject("Invalid Username or Password");
            }

        }, 2000);

    });

}

login("harshith", "1234")
    .then(message => console.log(message))
    .catch(error => console.log(error));
Enter fullscreen mode Exit fullscreen mode

This example helped me understand when to call resolve() and reject() based on a condition.


10. Key Takeaways

  • Callback Hell makes asynchronous code difficult to read and maintain.
  • Promises solve Callback Hell by making asynchronous code cleaner.
  • Every Promise has three states: Pending, Fulfilled, and Rejected.
  • JavaScript automatically provides the resolve() and reject() functions when creating a Promise.
  • resolve() changes a Promise to Fulfilled.
  • reject() changes a Promise to Rejected.
  • .then() handles successful results.
  • .catch() handles errors and rejected Promises.
  • .finally() executes regardless of success or failure.
  • Promise chaining makes complex asynchronous operations easier to manage.
  • Errors automatically propagate through the Promise chain until they are handled by the nearest .catch().

Conclusion

Today's learning helped me understand how JavaScript manages asynchronous operations using Promises.I learned why callback hell occurs, how Promises improve code readability, how Promise states work, how resolve() and reject() are provided by JavaScript, and how .then(), .catch(), .finally(), Promise chaining, and error propagation make asynchronous programming much cleaner and easier to maintain.

Understanding these concepts has also given me a much stronger foundation for learning async/await, since it is built on top of Promises.

Top comments (0)