Skip to main content

Command Palette

Search for a command to run...

JavaScript Promises: States and Methods

Updated
•8 min read•View as Markdown
JavaScript Promises: States and Methods
L
B.Tech Computer Science student focused on building a strong foundation in software engineering. Interested in data structures, system design, and modern web technologies. I write to learn, share, and document my journey as a developer.

As developers, one of the first confusing concepts we encounter in JavaScript is asynchronous behavior. Why doesn’t JavaScript wait for a task to finish? How does it handle results that come later? And what exactly is a Promise?

To understand this clearly, let’s step away from technical jargon and look at a simple, relatable scenario from Shinchan.

Imagine Shinchan asking his mother for ice cream. His mom, Misae Nohara, doesn’t give an immediate answer. Instead, she makes a condition: clean your room first. The final outcome—ice cream or no ice cream—depends on something that will happen in the future.

That simple agreement captures the core idea of a JavaScript Promise: a result that is not available right now, but will be resolved later—either successfully or unsuccessfully.

In this article, we will use this familiar scenario to understand how JavaScript Promises work, why they are important, and how their methods help manage asynchronous operations cleanly and efficiently.


The Ice Cream Contract

One hot afternoon, Shinchan demands chocolate ice cream. His mother, Misae Nohara, responds with a deal:

“If you clean your room properly, I promise I’ll give you ice cream.”

That statement is a real-world Promise.

Notice something important:
Misae does not immediately give or deny the ice cream. The outcome depends on a future action. Until she checks the room, the result is undecided.

This is exactly how a JavaScript Promise works.


What Is a JavaScript Promise?

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

It can be in one of three states:

  • Pending — The task is still in progress.

  • Fulfilled — The task completed successfully.

  • Rejected — The task failed.

In Shinchan case:

Promise State Real-World Meaning
Pending Shinchan is cleaning or pretending.
Fulfilled Ice-Delivered by mom as a reward.
Rejected No ice-cream because room was not cleaned. ( action was not fulfilled that are required by promise)

Creating a Promise

Here’s how Shinchan’s deal would look in JavaScript:

const iceCreamPromise = new Promise((resolve,reject) =>{
      let roomClean = true;
      setTimeout(() => {
        if(roomClean){
            resolve("Misae: Shinchan,here is your chocolate ice cream! ");
        }else{
            reject("No ice cream. Room is still messy.");
        }
    },2000);
});

What is happening here?

  • new Promise() creates the agreement (between shinchan and misae).

  • resolve() is called when the task succeeds(shinchan cleaned the room).

  • reject() is called when it fails(shinchan escape from house rather then cleaning).

  • setTimeout() simulates the time taken to inspect the room.

During those two seconds, the promise remains pending.


Handling the Outcome – The Scenario

Let’s continue the ice cream story from Shinchan.

Shinchan has “cleaned” his room. Now comes the most important moment — inspection time. Misae Nohara walks into the room to check.

At this point, the promise made to Shinchan is about to be settled.

There are only two possible outcomes.


Scenario 1: The Room Is Actually Clean (Fulfilled)

Misae looks around.

  • Toys are arranged.

  • Books are stacked.

  • No socks under the dinning table .

She nods and says:

“Good job. Let’s go get ice cream.”

This is the fulfilled state.

In JavaScript, this is handled using .then().

Meaning:
When the promise succeeds, we execute the success block.

Real-world reaction:
Shinchan celebrates, eats ice cream, and starts watching Action Kamen.

iceCreamPromise.then((message) => {
    console.log(message);
});

Scenario 2: The Room Is Still a Disaster (Rejected)

Misae enters the room.

  • Toys are scattered all over the room.

  • Dust is everywhere.

  • Nothing is properly cleaned.

She responds:

“No ice cream. Try again.”

This is the rejected state.

In JavaScript, this is handled using .catch().

Meaning:
When the promise fails, we execute the error-handling block.
Shinchan cries dramatically and blames Himawari.

iceCreamPromise.catch((error) => {
    console.log(error);
});

Scenario 3: Life Goes On (Finally)

Whether he gets ice cream or not:

  • The day continues.

  • Evening comes.

  • Dinner is served.

  • Misae still loves Shinchan.

This is like .finally() in JavaScript.

It runs no matter what the result is — success or failure.

iceCreamPromise.finally(() => {
    console.log("The day continues in Nohara family.");
});

Handling the outcome means preparing for both possibilities:

  • Success → reward

  • Failure → consequence

  • Either way → continue execution

That’s exactly how JavaScript Promises work.
They allow us to define what should happen when an asynchronous task succeeds, fails, or simply completes — just like Shinchan waiting for the final verdict.


Promise Chaining: One Action After Another

Suppose Shinchan plans the following:

  1. Get ice cream

  2. Eat it

  3. Watch TV

This sequence can be written using chained promises:

iceCreamPromise
    .then((msg) => {
        console.log(msg);
        return "Eating ice cream...";
    })
    .then((step) => {
        console.log(step);
        return "Watching TV Action Kamen";
    })
    .then((step) => {
        console.log(step);
    })
    .catch((err) => {
        console.log(err);
    });

Each .then() returns a new Promise, allowing structured, readable sequencing of asynchronous tasks.

This structure prevents deeply nested callbacks—commonly known as “callback hell.”


Handling Multiple Promises

Now imagine whole Kasukabe Defense Group is in shinchan's house and all want snacks.

  1. Promise.all() — Everyone Must have to be succeed Succeed
Promise.all([shinchanPromise, kazamaPromise, nenePromise, masaopromise, booPromise])
    .then(() => {
        console.log("All friends get snacks!");
    })
    .catch(() => {
        console.log("Someone failed. No snacks.");
    });

If any one promise fails, the entire result is rejected.


  1. Promise.race() — First One Wins
Promise.race([shinchanPromise, kazamaPromise, nenePromise, masaopromise, booPromise])
    .then((winner) => {
        console.log("First to finish gets extra ice cream!");
    });

The first promise to settle (fulfilled or rejected) determines the outcome.


  1. Promise.allSettled()

Purpose: Waits for all promises to complete (success or failure).

Promise.allSettled([shinchanPromise, kazamaPromise, nenePromise, masaopromise, booPromise])
    .then((results) => {
        console.log(results));
});

  1. Promise.any()

Purpose: Returns the first fulfilled promise.(whoever is first)

Promise.any([shinchanPromise, kazamaPromise, nenePromise, masaopromise, booPromise])
    .then(console.log)
    .catch(console.log);

Rule:

  • First success wins.

  • Fails only if all promises reject.

  • Throws AggregateError if all fail.


  1. Promise.reject()

Purpose: Returns a promise that is already rejected.

Promise.reject("Room not clean")
    .catch(console.log);

Used for: Creating an immediately failed promise.


  1. Promise.resolve()

Purpose: Returns a promise that is already fulfilled.

Promise.resolve("Ice cream ready")
    .then(console.log);

So far, we handled Shinchan’s ice cream deal using .then(), .catch(), and .finally(). That works perfectly—but modern JavaScript gives us a cleaner way to write the same logic: async / await.

Think of it as a more natural way to describe the same situation.

What Is async / await?

  • async makes a function automatically return a Promise.

  • await pauses execution inside that function until the Promise settles.

  • It does not block the entire program—only that function waits.

It makes asynchronous code look synchronous and easier to read.


Earlier, we handled the promise like this:

iceCreamPromise
    .then((msg) => {
        console.log(msg);
    })
    .catch((err) => {
        console.log(err);
    });

Now let’s rewrite it using async / await.

async function getIceCream() {
    try {
        const result = await iceCreamPromise;
        console.log(result);
    } catch (error) {
        console.log(error);
    } finally {
        console.log("The day continues in Kasukabe.");
    }
}

getIceCream();
  1. async function getIceCream()
    → This function now always returns a Promise.

  2. await iceCreamPromise
    → Shinchan waits for his mother’s decision.

  3. try block
    → If he cleaned properly (fulfilled), he gets ice cream.

  4. catch block
    → If he failed inspection (rejected), he handles disappointment.

  5. finally
    → Whether he wins or loses, the evening still happens.


Why async / await Is Preferred

Compared to chaining .then():

  • Cleaner structure

  • Easier to read

  • Better error handling with try/catch

  • Looks like normal synchronous code

Instead of stacking multiple .then() calls, you can write:

async function eveningPlan() {
    try {
        const iceCream = await iceCreamPromise;
        console.log(iceCream);

        console.log("Eating ice cream...");
        console.log("Watching TV...");
    } catch (error) {
        console.log(error);
    }
}

JavaScript Promises teach us how to handle results that arrive later without stopping the entire program. Using the example of Shinchan , we saw how an agreement is made first, the action happens next, and the outcome is handled when it’s decided.

With .then(), .catch(), and .finally(), we manage success and failure clearly. With async / await, we write the same logic in a cleaner, more readable way.

Mastering these concepts is essential for modern JavaScript development, because almost everything real-world—APIs, databases, authentication—depends on asynchronous behavior.