JavaScript has many ways to handle asynchronous calls and Promise is one of the mostly used built-in object nowadays.
so let’s understand what is Promise and how it works.
Promises:
- It’s a built-in object in JavaScript ES6 to handle asynchronous calls.
- Before Promises were introduced in ES6, callback functions were used to handle async calls.
- Promises provide a cleaner and more refined syntax to handle async calls.
Promise structure: (It has 2 parts)
- First part: Create the Promise and define conditions for success or failure of calls.
basic syntax:let promise1 = new Promise( (fn1, fn2) => { ...} );
- During instantiation of the object using new keyword, it takes a function (fn1, fn2) as an argument which itself has 2 arguments as functions f1 and f2.
- We can name them anything as these are just functions but resolve & reject, is the standard naming for better understanding.
let promise1 = new Promise((resolve, reject) => { ... });
- So resolve and reject arguments in Promises are nothing but callback functions.
- These functions accept no argument or a single argument (Of type string, number, boolean, array or object).
- On resolve(), it executes then() method and on reject(), it executes catch() method.
- Second part: Describes action to take when a successful condition is met with the resolve() definition.
Also, an action to take when a failure condition is met with the reject() definition.promise1.then( (message) => { // Execute code inside this on resolve(). }).catch( (message) => { // Execute code inside this on reject(). });
Example: ( jsfiddle links: reject() example and resolve() example )
In below code, change isCallResolve to true or false to see the action for resolve() and reject() calls.// First part: Create the Promise and define conditions for success or failure. let promise1 = new Promise( (resolve, reject) => { let isCallResolve = false; if (isCallResolve) { resolve('Promise is successful and resolve method is called that is code inside then() !'); } if (!isCallResolve) { reject('Promise failed and reject method is called that is code inside catch() !'); } }); // Second part: Define the actions to take when the conditions are fulfilled as success or failure. promise1.then( (message) => { // then() method is the definition for resolve() callback method. // Execute code inside here if the promise is resolved i.e. is successful. console.log(message); }).catch( (message) => { // catch() method is the definition for reject() callback method. // Execute code inside here if the promise is is rejected that is not successful. console.log(message); });
Promise properties:
- The resulting promise object has two internal properties:
- state: Set to ‘pending’ during initialization and then changes to either ‘fulfilled’ or ‘rejected’ after the execution.
- result: value. Initially set to undefined.
- After execution, it calls either resolve() or reject() based on success or failure.
- resolve(value): Indicates that the execution has finished successfully.
- state: set to ‘fulfilled’.
- result: set to the value.
- reject(error): Indicates that an error has occurred.
- state: set to ‘rejected’.
- result: set to an error.
- resolve(value): Indicates that the execution has finished successfully.
- See the below screen for a different state of promise before and after execution.
Promise.finally():
- finally() always runs when the promise finishes its execution no matter it’s a resolve or reject.
- It is useful for cases where we want to execute some code anyhow after resolve or reject. For example, variable cleanup, stop loader after async calls, close popups etc.
Example: ( jsfiddle link: promise.finally() example )new Promise(function(resolve, reject) { setTimeout(() => resolve(5), 1000); }).then( (result) => { alert('Resolve executed'); }).catch( (result) => { alert('Reject executed'); }).finally( (result) => { alert('Finally will execute no matter it is a resolve or reject'); });
Promises chaining:
- Multiple promises can be chained together with .then() one after another and pass the result of the previous promise to the next Promise, and so on.
- It is useful to handle multiple async calls to execute one task after another by using the result of the previous async call.
- Below example shows that the next promise is called only after the success of previous promise and uses the result from the previous promise.
Example: ( jsfiddle link: Promise chaining example )new Promise(function(resolve, reject) { setTimeout(() => resolve(5), 1000); }).then( (result) => { // Promise #1 alert(result); // 5 return result * 3; }).then( (result) => { // Promise #2 alert(result); // 15 return result * 4; }).then( (result) => { // Promise #3 alert(result); // 60 return result * 2; });
Promise.all():
- It evaluates all Promises and executes .then() only after all the Promises within the array list finishes is execution.
Example: ( jsfiddle link: promise.all() example )
It executes the .then() method only after the successful execution of all other three promises.
promise3 has 3 seconds delay so it will wait for promise3 to finish its execution.let isCallResolve = true; let promise1 = new Promise((resolve, reject) => { if (isCallResolve) { resolve('promise1 successfully executed.'); } }); let promise2 = new Promise((resolve, reject) => { if (isCallResolve) { resolve('promise2 successfully executed.'); } }); let promise3 = new Promise((resolve, reject) => { setTimeout(()=> { resolve('promise3 successfully executed.'); }, 3000); }); Promise.all([promise1, promise2, promise3]).then( (message) => { console.log(message); });
Promise.race():
- It doesn’t wait for all the Promises and executes as soon as anyone promise from the array list completes its execution.
Example: ( jsfiddle link: promise.race() example )
It will output result from promise1 or promise2 whichever executes first.
The result from promise3 will not output as there is a delay of 2 seconds for its execution so it will not wait for promise3 to complete its execution.let isCallResolve = true; let promise1 = new Promise((resolve, reject) => { if (isCallResolve) { resolve('promise1 successfully executed.'); } }); let promise2 = new Promise((resolve, reject) => { if (isCallResolve) { resolve('promise1 successfully executed.'); } }); let promise3 = new Promise((resolve, reject) => { setTimeout(()=> { resolve('promise3 successfully executed.'); }, 2000); }); Promise.race([promise1, promise2, promise3]).then( (message) => { console.log(message); });
Real world implementation to see Promise in action:
- Promise.race():
Example: ( jsfiddle link: promise.race() real-world example )
Here we are calling 2 open APIs, jsonplaceholder and GitHub.
With race() method, the promise which finishes its execution first will return the output and will not wait for any other promises to complete its execution provided in the array.let promise1 = new Promise((resolve, reject) => { let request = new XMLHttpRequest(); request.open('GET', 'https://jsonplaceholder.typicode.com/todos/1'); request.send(); request.onreadystatechange = () => { if (request.readyState === 4) { console.log('jsonplaceholder API, request status: ' + request.status, ', readyState: ' + request.readyState); resolve('jsonplaceholder API responded faster!'); } } }); let promise2 = new Promise((resolve, reject) => { let request = new XMLHttpRequest(); request.open('GET', 'https://api.github.com/users/mojombo/repos'); request.send(); request.onreadystatechange = () => { if (request.readyState === 4) { console.log('GitHub API, request status: ' + request.status, ', readyState: ' + request.readyState); resolve('GITHUB API responded faster!'); } } }); Promise.race([promise1, promise2]).then((message) => { console.log(message); });
- Promise.all():
Example: ( jsfiddle link: promise.all() real-world example )
Here we are calling 2 open APIs, jsonplaceholder and GitHub.
With all() method, it will wait for both promises to finish its execution and output from both will be returned.let promise1 = new Promise((resolve, reject) => { let request = new XMLHttpRequest(); request.open('GET', 'https://jsonplaceholder.typicode.com/todos/1'); request.send(); request.onreadystatechange = () => { if (request.readyState === 4) { console.log('jsonplaceholder API, request status: ' + request.status, ', readyState: ' + request.readyState); resolve('jsonplaceholder API responded!'); } } }); let promise2 = new Promise((resolve, reject) => { let request = new XMLHttpRequest(); request.open('GET', 'https://api.github.com/users/mojombo/repos'); request.send(); request.onreadystatechange = () => { if (request.readyState === 4) { console.log('GitHub API, request status: ' + request.status, ', readyState: ' + request.readyState); resolve('GITHUB API responded!'); } } }); Promise.all([promise1, promise2]).then((message) => { console.log(message); });
I hope with this post, you will get some understanding of what is Promise and how it works in JavaScript.
Happy coding!!!