DEV Community

Cover image for Promises - JavaScript Concepts Simplified
Thisura Thenuka
Thisura Thenuka

Posted on • Updated on • Originally published at simplecoder.hashnode.dev

Promises - JavaScript Concepts Simplified

What is a Promise?

By definition, a promise is a declaration that one will do something or that a particular thing will happen. In the programming world, a promise is an object that represents the eventual completion (or failure) of an asynchronous operation.

States

A promise can have the following states.

  1. Pending (Initial State, neither fulfilled nor rejected)
  2. Fulfilled (The operation was a success)
  3. Rejected (The operation was a failure)

A pending promise can either be fulfilled or rejected. We can attach handler methods to handle the events of promises getting fulfilled and rejected. We can use the then method in promises to attach these handlers.

promise.then(handleSuccess, handleRejection)
Enter fullscreen mode Exit fullscreen mode

We have attached two handler methods to the above promise. Now, if the promise gets fulfilled, handleSuccess will be called and if it gets rejected, the handleRejection method will be called.

When a promise is not in the pending state, we say the promise is settled. Please keep in mind that being settled is merely a linguistic convenience, not a state.

Methods

In addition, promise objects have multiple methods that can be really useful when handling promises.

1. Promise.all()

Input - An iterable of promises
Output - A single promise that resolves into an array of the results of the input promises

This method is useful when you have more than one promise and you want to do something only when all the operations are completed successfully. Promise.all() will reject immediately upon any of the input promises rejecting. For example, if you want to make multiple API calls and the code relies on all of them to be successful, you can use Promise.all().

const firstPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, "First Promise");
});

const secondPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Second Promise");
});

const thirdPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "Third Promise");
});

const rejectedPromise = Promise.reject("EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED");

//Promise.all() method
Promise.all([firstPromise, secondPromise, thirdPromise])
  .then((results) => {
    console.log("All the promises were fulfilled here - ", results);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });

//Promise.all() method with a rejected promise
Promise.all([firstPromise, rejectedPromise, thirdPromise])
  .then((results) => {
    console.log("All the promises were fulfilled");
    console.log("Response from all method - ", results);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });
Enter fullscreen mode Exit fullscreen mode

Output

Error:  EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED
All the promises were fulfilled here -  [ 'First Promise', 'Second Promise', 'Third Promise' ]
Enter fullscreen mode Exit fullscreen mode

2. Promise.allSettled()

Input - An iterable of promises
Output - A single promise that resolves into an array of the results of the input promises

We can use this method when the tasks are dependent on each other and you want to know all the results of all the promises regardless of the result of each promise.

//Promise.allSettled() method with a rejected promise
Promise.allSettled([firstPromise, rejectedPromise, thirdPromise])
  .then((results) => {
    console.log("I don't care if all the results are fulfilled or not");
    console.log("Response from allSettled method - ", results);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });
Enter fullscreen mode Exit fullscreen mode

You can clearly see the difference between the all and allSettled methods. The all method wants all the promises to be fulfilled while allSettled method only wants the promises to be settled (regardless of fulfilled or rejected)

I don't care if all the results are fulfilled or not
Response from allSettled method -  [
  { status: 'fulfilled', value: 'First Promise' },
  {
    status: 'rejected',
    reason: 'EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED'
  },
  { status: 'fulfilled', value: 'Third Promise' }
]
Enter fullscreen mode Exit fullscreen mode

3. Promise.any()

Input - An iterable of promises
Output - A single promise that resolves as soon as any of the promises in the iterable fulfils, with the value of the fulfilled promise

If none of the promises gets fulfilled, then the returned promise is rejected with an AggregateError.

const firstPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, "First Promise");
});

const secondPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Second Promise");
});

const thirdPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "Third Promise");
});

const rejectedPromise = Promise.reject(
  "EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED"
);

//Promise.any() Method
Promise.any([firstPromise, secondPromise])
  .then((fasterPromise) => {
    console.log("Response from any method with no rejections - ", fasterPromise);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });

//Promise.any() Method with rejections
Promise.any([rejectedPromise, rejectedPromise])
  .then((fasterPromise) => {
    console.log("Response from any method with rejections - ", fasterPromise);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });


//Promise.any() Method
Promise.any([firstPromise, secondPromise]).then((fasterPromise) => {
  console.log("Response from any method - ", fasterPromise);
});
Enter fullscreen mode Exit fullscreen mode

Output

Error:  [AggregateError: All promises were rejected]
Response from any method with no rejections -  Second Promise
Enter fullscreen mode Exit fullscreen mode

4. Promise.race()

Input - An iterable of promises
Output - A promise that fulfils or rejects as soon as one of the promises in an iterable fulfils or rejects, with the value or reason from that promise

const firstPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, "First Promise");
});

const secondPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Second Promise");
});

const thirdPromise = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "Third Promise");
});

const rejectedPromise = Promise.reject(
  "EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED"
);

//Promise.race() Method
Promise.race([firstPromise, secondPromise])
  .then((fasterPromise) => {
    console.log(
      "Response from race method with no rejections - ",
      fasterPromise
    );
  })
  .catch((error) => {
    console.error("Error: ", error);
  });

//Promise.race() Method with rejections
Promise.race([secondPromise, rejectedPromise])
  .then((fasterPromise) => {
    console.log("Response from race method with rejections - ", fasterPromise);
  })
  .catch((error) => {
    console.error("Error: ", error);
  });
Enter fullscreen mode Exit fullscreen mode

As the name of the method suggests, we have a race here. It does not matter whether the promises are fulfilled or rejected. The race method returns the fastest settled promise.

Output

Error:  EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED
Response from race method with no rejections -  Second Promise
Enter fullscreen mode Exit fullscreen mode

5. Promise.reject() and Promise.resolve()

You are already familiar with the reject method since I’ve used it in earlier examples. Basically, we use the reject method to reject a promise.

In addition, we have the resolve method which returns a promise that is resolved with the given value, or the promise passed as value, if the value was a promise object. Resolved is not to be confused with fulfilled. Please read this StackOverflow answer to learn more about it.

Fates

Promises also have two mutually exclusive fates, resolved and unresolved.

  1. If we try to resolve or reject a promise and it has no effect, we say the promise is resolved.
  2. If we try to resolve or reject a promise and it has an effect, we say the promise is unresolved.

Conclusion

To sum things up, we use promises to handle asynchronous operations in JavaScript. Especially, when there are multiple asynchronous operations running, it would be a nightmare to handle them if you don't know about promises.

I hope you learned something valuable from Today's article. If you liked it, drop a like and follow me so that you don't miss the upcoming articles. And as always, stay safe guys 😷

Top comments (0)