Wtf is Promises and why it slaps?😏

Namrata ThakurNamrata Thakur
5 min read

Do you remember how you were so excited to learn JavaScript, especially that initial topics like loops and functions and suddenly they started teaching you callback hell, promises and async/await and you sat there like….woah what??!!! Wait i can’t understand?? Yeah we all have been through that.

Let me explain. But before that don’t look at this post and be like “Why would i listen to you?” (Did you read that in meme style?). Honestly your question is not wrong, why would yo listen to me? Honestly idk man i mean you have probably watched millions of tutorials and you still didn’t understand so what’s the harm in one more.

Before that let’s review Callback hell(no bs style)

  • Callbacks - Functions that are passed as arguments to another function.

  • Why use callbacks? - To handle asynchronous tasks.

  • Callback hell - Too many nested callbacks inside each other creating a pyramid of doom, your code becomes hard to read and understand.(and debug)

  • Why it happens? When you are trying to handle multiple async tasks.

  • What is async tasks - tasks that are not directly handled by JS main thread.(cause JS is single threaded - does task one at a time(this in English is known as synchronous) but when JS throws tasks that require a lot of time(fetching data, reading files, or waiting for timers) to web APIs these tasks becomes Asynchronous))

Promises:-

What do you mean by Promises in english? When i say “I promise to return your book tomorrow”, there are three possibilities:-

  • I will give you your book in the future(Yay!)

  • I didn’t gave you your book :(

  • It’s still today

This is exactly what promises is in JS. It is an object that represents a task that will be completed in future. Based on the example i gave Promises in JS also have 3 states:-

  • Resolved(fulfilled) - Promise has been successfully completed

  • Reject - Something went wrong

  • Pending - The promise is still waiting for a resolution.

That’s it babe, let’s look at the syntax:-

let myPromise = new Promise((res, rej) => { //res stands for resolve and rej for reject
let success = True; // let's suppose i gave you the book
if(success) {
res("Task completed"); //Promise resolved yay
} else {
rej("Task rejected"); //promise rejected noooo
}
});
myPromise.then((message) => //if resolved .then() will run
console.log(message))
.catch((error) => //if reject .catch() will run
console.log(error));

Let’s look at an example, I am Simulating a Delayed Task ( using setTimeout)

function delayTask() {
    return new Promise((res) => {
        setTimeout(() => {
            resolve("Task done after 2 seconds!");
        }, 2000);
    });
}

// Using the promise
delayTask().then((message) => console.log(message));

Output after 2 sec - Task done after 2 seconds!.

How it works?

  • The Promise starts, but JavaScript does not wait(non-blocking nature of JS cause it’s single threaded).

  • After 2 sec, res() runs, and .then() prints the message.


Let me give you one more example that my teacher gave (shout out to him) - Fetching Data (we are Simulating an API Call)

function getUserData() {
    return new Promise((res, rej) => {
        console.log("Fetching user data...");

        setTimeout(() => {
            let success = true;  // Simulate success 

            if (success) {
                resolve({ name: "Anika", age: 21 });
            } else {
                reject("Failed to fetch user data!");
            }
        }, 3000);
    });
}

// Using the promise
getUserData()
    .then((user) => console.log("User data:", user)) 
    .catch((error) => console.log(error));

“Okay that’s interesting but why do i wanna use it?” Good question dude let’s first understand one more topic which is Promise chaining.

Sometimes, we need to run multiple asynchronous task one after another, where each task depends on the previous one. Promise chaining allows us to connect multiple .then() methods so each one runs after the previous promise resolves.


function step(time, message) {
    return new Promise((res => { //only using res because its highly unlikely that the timer will reject something 
        setTimeout(() => {
            console.log(message);
            res();
        }, time);
    });
}
step(1000, "Step 1") //
    .then(() => step(1000, "Step 2"))
    .then(() => step(1000, "Step 3"))
    .then(() => step(1000, "Step 4")); //since i didn't wrote rej i am not writing .catch

Now if i want to write this same code using callbacks it would look like this:-

setTimeout(() => {
    console.log("Step 1");
    setTimeout(() => {
        console.log("Step 2");
        setTimeout(() => {
            console.log("Step 3");
            setTimeout(() => {
                console.log("Step 4");
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000); //This is what callback hell looks like, i really hope you are not tearing up by reading this

In promise chaining we don’t use multiple .catch() - one is placed at the end of the chain to catch errors from any step. If you want, you can add individual .catch() after each .then() for step-specific error handling.

function getUser() {
    return new Promise((res) => {
        setTimeout(() => {
            console.log("User data fetched!");
            res({ id: 1, name: "Anika" });
        }, 1000);
    });
}

function getPosts(user) {
    return new Promise((res, rej) => { // Adding reject case
        setTimeout(() => {
            console.log(`Failed to fetch posts!`);
            rej("Error: Could not fetch posts");
        }, 1000);
    });
}

function getComments(posts) {
    return new Promise((res) => {
        setTimeout(() => {
            console.log("Comments on posts fetched!");
            res(["Comment A", "Comment B"]);
        }, 1000);
    });
}
getUser()
    .then(getPosts)
    .then(getComments)
    .then((comments) => console.log("Final Data:", comments))
    .catch((error) => console.log("Error caught:", error)); // Handle errors

What about .finally?

  • Its a method that runs after a promise is settled, it dosen’t care if the promise was resolved or rejected.

  • Why use it? To clean up resources (like closing a loading spinner or resetting UI)

Syntax:-

promise
    .then((res) => console.log("Success:", result))
    .catch((error) => console.log("Error:", error))
    .finally(() => console.log("Cleanup done!")); //does not receive any arguments

Now you might be wandering “Hmm then Promises are the GOAT” well not exactly.

  • Promises are harder to manage. How?

  •   fetchData()
          .then((data) => {
              return processData(data);
          })
          .then((processedData) => {
              return saveData(processedData);
          })
          .then((result) => {
              console.log("Data saved:", result);
          })
          .catch((error) => {
              console.log("Error:", error);
          }); //Each .then adds another layer => If the code has lot of steps it becomes harder to read, debug and maintain
    

    So we usually go for another topic called async/await, but that’s a topic for another day.

  • I hope this article made Promises atleast 1% easy. Till then keep coding and keep vibing.

1
Subscribe to my newsletter

Read articles from Namrata Thakur directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Namrata Thakur
Namrata Thakur