What is Promise?
Promise is a object that is used as a placeholder for future value. What it means is that we do not know when our operation is going to conclude and what is the value(outcome) of our operation going to be so we create a placeholder to store that value when it becomes available.
Promise exists in 1 of 3 states at any given time.
- Pending(by default)
- Fulfilled(Resolved)
- Rejected
Initially Promise is always in pending state. It means the operation has not finished.
Once the operation completes, promise is considered settled because it is not in pending state anymore. When it is settled, it is either in fulfilled or rejected state.
If the operation is completed successfully, it will be in fulfilled state.
If there was an error in operation, it will be in rejected state.
Creating Promise
We create Promise by calling Promise as a constructor function.
const promiseExample = new Promise(function(resolve, reject){ }); // promiseExample is a promise object.
Promise constructor is called with an argument. It is called executor. It accepts two parameters resolve and reject. These are functions called by executor and you can name it anything but they are commonly named resolve and reject.
Resolve function is called when the operation is successfully completed.
Reject function is called when the operation fails to complete.
Both resolve and reject function accepts single argument.
Argument passed to resolve function represents either fulfilled value from our operation or another promise object that will provide fulfilled value if the operation succeeds.
Argument passed to reject function represents rejected value of the promise.
All promise object has then and catch method.
then() method is called with a value passed that was passed to resolve function. You can call method without any argument also.
catch() method is called with a reject value of reject function. You can also call catch method without any argument.
const promiseExample = new Promise(function(resolve, reject){ setTimeout(function (){ if(Math.random() > .5){ resolve(‘More than’); }else{ reject(‘Less than’); } },1000); }); promiseExample.then(function(result){ console.log(result) }) .catch(function(result){ console.log(result); })
Here we have a promise object (promiseExample), that gets resolved if the Math.random is greater than 0.5 and gets rejected if it is less than 0.5 after 1000 millisecond delay.
If it gets resolved, resolve function is passed with string ‘More than’ and it’s state changes to resolved.
If rejected, reject function is passed with string ‘Less than’ and it’s state is changed to rejected.
And when promise state changes, fulfillment and rejection is handled with then and catch method respectively.
- then() method interacts with resolve value.
- catch () method interacts with reject value.
Remember that if you don’t handle rejection(error), it will occur silently behind the scene.
Let’s summarize what we have learned so far.
1. When promise is created, its status is pending and value undefined.
[[PromiseStatus]]: “pending”
[[PromiseValue]]: undefined
2. Here, promise is resolved with no value passed to resolve so status is resolved but value is undefined.
let promiseExample = new Promise(function(resolve,reject){ resolve(); });
[[PromiseStatus]]: “resolved”
[[PromiseValue]]: undefined
3. Here, promise is resolved with value.
let promiseExample = new Promise(function(resolve,reject){ resolve(“resolve value”); });
[[PromiseStatus]]: “resolved”
[[PromiseValue]]: “resolve value”
4. Here, promise is rejected with value.
let promiseExample = new Promise(function(resolve,reject){ reject("unresolved"); }); promiseExample.catch(function(result){ console.log(result); });
[[PromiseStatus]]: “rejected”
[[PromiseValue]]: “unresolved”
Chaining Promises
When you call then() and catch() method, it returns another promise with then() and catch() method. We can resolve this another promise with then() method but this promise can only be resolved after previous promise is resolved.
let promiseExample = new Promise((resolve,reject) =>{ resolve("chain promise") }); promiseExample.then((result) =>{ return result }) .then((result) =>{ console.log(result) }); //We are chaining promises and passing value from one promise to another.
Here promiseExample.then() returns another promise so we can attach then() method on it. then() method is called only after first promise is resolved or rejected (resolved in our case).
Handling Multiple Promises
Promise.all()
let fruit1 = new Promise((resolve, reject) =>{ resolve("apple"); }) .then((result) =>{ console.log(result) }) let fruit2 = new Promise((resolve, reject) =>{ resolve("banana"); }) .then((result) =>{ console.log(result) }); let fruit3 = new Promise((resolve, reject) =>{ resolve("mango"); }) .then((result) =>{ console.log(result) })
Here, we created 3 promises(fruit1, fruit2,fruit3) with resolved values.
Instead of calling then() method on each promises, we can handle multiple promises with Promise.all() method.
We can pass a single array of promises to Promise.all() method and return promise that will be resolved once all the promises are resolved or return promise that is rejected if one single promise fails. This is called fail fast behavior.
let fruit1 = new Promise((resolve, reject) =>{ resolve("apple"); }) let fruit2 = new Promise((resolve, reject) =>{ resolve("banana"); }) let fruit3 = new Promise((resolve, reject) =>{ resolve("mango"); }) let fruits = Promise.all([fruit3,fruit2,fruit1]); fruits.then((result) =>{ console.log(result[0]); console.log(result[2]); console.log(result[1]); }); //mango // banana //apple Promise {<resolved>: Array(3)} [[PromiseStatus]]: "resolved" [[PromiseValue]]: Array(3) 0: "mango" 1: "banana" 2: "apple"
Here, fruits (promise object) is resolved with an array with value of promises(fruit3,fruit2,fruit1) in the same order they were passed in (let fruits = Promise.all([fruit3,fruit2,fruit1]) ).