Promises - JavaScript Concepts Simplified
Thisura Thenuka
Posted on February 12, 2022
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.
- Pending (Initial State, neither fulfilled nor rejected)
- Fulfilled (The operation was a success)
- 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)
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);
});
Output
Error: EVERYBODY STAY CALM. STAY F***ING CALM. I WAS REJECTED
All the promises were fulfilled here - [ 'First Promise', 'Second Promise', 'Third Promise' ]
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);
});
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' }
]
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);
});
Output
Error: [AggregateError: All promises were rejected]
Response from any method with no rejections - Second Promise
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);
});
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
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.
- If we try to resolve or reject a promise and it has no effect, we say the promise is resolved.
- 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 😷
Posted on February 12, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.