Skip to main content

Command Palette

Search for a command to run...

Keeping Promises in JavaScript (Unlike Real Life)

Published
7 min read
Keeping Promises in JavaScript (Unlike Real Life)

Life Doesn’t Pause. Neither Does JavaScript.

When ordering food in a cafe, you go to the counter, place an order by making payment, and the cashier hands you a buzzer. You don't stand in front of the cashier while your order is being prepared, blocking the queue for everyone after you. You take the buzzer and sit down, scrolling through your phone until your order is ready. You are doing other tasks while the food is cooking in the background. When the food is ready, the buzzer rings and signals you to collect the food at the counter (this is promise resolving!), and now you go get the food. JavaScript deals with waiting the same way.

What Promises exactly are:

A Promise in JavaScript represents that an block of code will eventually be completed or failed and also represents the result value.

A promise has 3 states being:

  1. Pending -> The initial state where the tasks has not yet been completed.

  2. Fulfilled -> The task has been completed successfully.

  3. Rejected -> The task has failed.

function prepareOrder(dish) {
  return new Promise((resolve, reject) => { 
    setTimeout(() => {
      if (!dish) {    //if no dish(argument) has been passed, reject state
        reject(new Error("No dish is there"));
        return;
      }
      console.log(`${dish} is ready`);
      resolve({ dish, status: "prepared" });
    }, 100);                //100 milli-second of delay has been added
  });
}

Promise.all()

The scenario:

Imagine a group chat is planning a weekend trip to Goa. For this trip to happen, there are four friends who want to go but there are certain conditions before going.

  1. Ayush -> needs to get permission from his parents.

  2. Himanshu -> needs to get his money back from a friend who he lended money, to fund hotels.

  3. Ishan -> needs to bring his car.

  4. Raj -> needs to just show-up. (he is lazy)

If Ayush and Himanshu are ready to go, but Ishan says that he cant get the car, then the whole plan is completely dead. You can't go on a partial trip without a car. It's everyone, or it's no one.

Translating the analogy to JavaScript

Promise.all() works in a similar way, where the group chat is Promise.all([Ayush, Himanshu, Ishan]) and .then is every single friend confirming that he is coming and the trip is officially on.

The Rejection (.catch): This is the important part of Promise.all() . As soon as Ishan says he can't get his car, the plan is cancelled immediately. The group chat doesn't care anymore if Ayush got permission or if Raj is coming. This is when .catch triggers and none of the other promises resolve.

const Ayush = Promise.resolve("Got permission, Let's go!.")
const Himanshu = Promise.resolve("Got the money! ")
const Ishan= Promise.reject("Couldn't get the car, Abba nahi maane 😭")
const Raj = Promise.resolve("Bags Packed, I am ready to go!")

Promise.all([Ayush, Himanshu, Ishan, Raj])
  .then((confirmations) => {
    console.log("The Plan is ON!, Pack your bags!")
  })
  .catch((reasons)=>{
    console.log("Trip cancelled because: ", reasons)
  })
//Output: Trip cancelled because: Couldn't get the car, Abba nahi maane 😭

If everything goes as planned and trip happens, then the result will be returned in an array of 'confirmations' in the same order as input.

Promise.allSettled() :

Acquiring food at a Wedding Buffet:

Imagine you are at a wedding along with three of your cousins. The buffet just opened and you have planned that each of you will go for different foot stalls to get every food fast.

  • Cousin A goes for Live starter counter (Returns with Panner tikkas = fulfilled)

  • Cousin B goes for the main course, but the tandoor broke down and he gets nothing (Fails to get Naan = rejected)

  • Cousin C successfully secures Gulab Jamun (Returns with Gulab Jamuns = fulfilled)

If this was Promise.all() you would leave the wedding buffet as soon as tandoor broke down and Cousin B fails to get Naan and be starving, but here we use Promise.allSettled() here, you wait for all cousins to return with whatever they managed to get and just eat whatever survived the chaos.

const cousinA= Promise.resolve("Paneer Tikka secured");
const cousinB = Promise.reject("Tandoor broke down");
const cousinC = Promise.resolve("Gulab Jamun acquired");

Promise.allSettled([cousinA, cousinB , cousinC ])
  .then((plate) => {
    console.log("Time to eat! Here is what we got:");
    console.log(plate);
  });

/* OUTPUT:
Time to eat! Here is what we got:
[
  { status: 'fulfilled', value: 'Paneer Tikka secured' },
  { status: 'rejected', reason: 'Tandoor broke down' },
  { status: 'fulfilled', value: 'Gulab Jamun acquired' }
]
*/

Promise.allSettled() executes all the promise statements, both resolved and rejected. It does not care resolve or reject, it returns the result in an array in the same sequence as the original promise array.

Promise.race() :

The Scenario:

Imagine it is 2:00am and you are feeling lonely and you make the most chaotic decision of texting three of your exes with the same text "you up?". You hit send on all three at the exact same time.

  • Ex A (The fast replier): Instantly replies in 0.5 seconds: "Do not ever contact me again, I am calling the police."(rejected)

  • Ex B: Is currently typing (will resolve in 3 seconds with "I've missed you.")

  • Ex C: Is fast asleep (will resolve in 5 hours with "Hey, just woke up, come over.")

Because Ex A was the fastest to reply, she wins the race.

Promise.race() takes the brutal rejection as the final answer for the entire night. It instantly throws your phone out of the window, you cry yourself to sleep all alone and lonely.

const exA = new Promise((_, reject) => {
  setTimeout(() => {
    reject("Blocked and reported");
  }, 500); //0.5 seconds
});

const exB = new Promise((resolve) => {
  setTimeout(() => {
    resolve("I missed you too <3");
  }, 3000); //3 seconds
});

const exC = new Promise((resolve) => {
  setTimeout(() => {
    resolve("Come over");
  }, 18000000); // 18,000,000ms = 5 hours
});

Promise.race([exA, exB, exC])
.then((reply)=>{
  console.log("Success! You got a late night date: ", reply)
  return
})
.catch((disaster) =>{
  console.log("Night ruined. Final result:", disaster)
}
)

// Output: Night ruined. Final result: Blocked and reported 
// (Notice how Ex B and Ex C are completely ignored because Ex A was faster)

This is exactly how Promise.race() works, race does not care if the promise is resolved or rejected, it just wants the promise which executes the fastest, Everything else that finishes after that is completely ignored.

Promise.any() :

The Scenario:

Suppose you are standing on the side of the road during evening rush hour. You need to get home, so you walk up to a line of three parked auto-rickshaws and ask them all if they will go to your area.

  • Auto 1 (The VIP): Looks at you, shakes his head without saying a word and drives off (rejected instantly)

  • Auto 2 (The Scammer): Tells you, "Meter se nahi jaunga, 300 lagenge." You refuse. (rejected after negotiation)

  • Auto 3 (The Legend): Just nods, resets the meter and says, "Baith Jao." (fulfilled)

We do not care how many drivers rejected as long as atleast one of them says yes.

const auto1 = new Promise((_,reject) => setTimeout(() => {
  reject("Silently drives away")
}, 1000))
const auto2 = new Promise((_,reject) => setTimeout(() => {
  reject("Demands double the meter rate!")
}, 3000))
const auto3 = new Promise((resolve) => setTimeout(() => {
  resolve("Meter on. Let's Go!")
}, 5000))

Promise.any([auto1, auto2, auto3])
.then((ride)=>{
  console.log("Success! You got a ride home:", ride)
})
// Output: Success! We got a ride home: Meter on. Let's go! 
// Notice how it completely ignored the first two rejections!

Promise.any() ignores the number of rejections as long as there is at least one resolution. It fails only if all promises fail.

Choosing the Right Promise Strategy:

  • Promise.all() is strict. It needs all of the promises to resolve and collapses even after one rejection

  • Promise.allSettled() is realistic. It reports everything, whether resolution or rejection. It doesn't care.

  • Promise.race() is impatient. The first outcome decides everything, be it rejection or fulfillment.

  • Promise.any() is stubborn. It gives up when everyone says no.

In asynchronous JavaScript, the problem is not waiting,

it is waiting the wrong way.