JS-Promises-Featured_image
UI Development

JavaScript Promises


Javascript_Promise

promise is an object representation of the eventually completed (or failure) asynchronous operation along with its resulting value. Promises are easy to manage when we (developers) are dealing with the asynchronous operation where callbacks can create callback hell leading to the complex code structure which is very difficult to understand.

Promises are not the first choice, prior to that we use events and the callback function but they had limited functionalities as, the multiple callback functions may create callback hell and the events are also not so good in handling the asynchronous operation and hence they both lead to the complex structure of the code. But after the introduction of the promises in ES6 this is the best choice for the developer when dealing with the asynchronous operations in the simplest manner.

Before we get in to the technical stuff, lets understand the terminologies of the Promises.

States of Promises:

A promise can be:

  1. Resolved or Fulfilled: – A promise is resolved if its result is available. Simply means something is finished and all went well. (onFullfilled() will be called) e.g., resolve() was called.
  2. Rejected: – A promise is rejected if an error happened. (onRejected() will be called) e.g., reject() was called.
  3. Unresolved or Pending: – A promise is pending if the result is not ready. That is, its waiting for something to finish first. (e.g., an asynchronous operation)
  4. Settled: – A promise is settled if it’s not pending i.e. when it is resolved or rejected.

A settled promise is immutability i.e. once settled a promise can’t be resettled.

Creating a Promise:

Syntax:

const promise = new Promise((resolve, reject)  => { 
	//do something 
}) ;

A promise is created by using the Promise constructor. Promises are eager, it means it start doing whatever task you give it as soon as the Promise constructor is invoked. 

Parameters

  • Promise constructor takes only one argument, a callback function.
  • That callback function takes two argument, resolve and reject.
  • Perform operations inside the callback function and if everything went well then call resolve.
  • If desired operations do not go well then call reject.

Example:

const promise = new Promise((resolve, reject) => {   
	const randomNumber = Math.floor(Math.random() * 10);  
 	setTimeout(() => { 
      if(randomNumber < 6) { 
      resolve(‘All things went well!’); 
   } else { 
      reject('Something went wrong'); 
   } 
  }, 2000); 

});

Most of the time we (developer) will be consuming promises rather than creating promises. 

Consuming a Promise

Now we know how we can create Promises lets understand how can we consume the promises. We can consume a Promise by registering functions using .then() and .catch() methods. 

  1. .then()

This method is evoked when a promise is either resolved or rejected. 

Parameters:

  • First callback function is executed if the promise has some resulting value i.e. resolved value.
  • Second callback function is executed when some error is received. (This callback function is optional)

Syntax:

.then(function(result) { 
	//handle Success 
}, function(error) { 
	//handle error 
})

Example:

const promise = new Promise((resolve, reject) => { 
	resolve(‘Hey, I am resolved’); 
}) 
promise 
.then((successMessage) => { 
	//Success handler function is invoked 
	console.log(successMessage); 
},(errorMessage) => { 
	console.log(errorMessage); 
})
Output: – Hey, I am resolved 

2. .catch()

This method is evoked when a promise either rejected or some error has occurred during its execution.

Parameters:

  • A callback function which execute to handle errors or rejections. 

Syntax:

.catch((error) => { 

	//handle error 

})

Example:

const promise = new Promise((resolve, reject) => { 
	reject(“Some Error occurred during execution”); 
}) 
Promise 
.then((successMessage) => { 
	console.log(successMessage); 
}) 
.catch((errorMessage) => { 
	//error handler function is invoked 
	console.log(errorMessage); 
});
Output: – Some Error occurred during execution 
Note: – catch() method internally calls then(null, errorHandle), i.e. catch is just sugar for then(null, errorHandle). 

Promise Chaining

The then(method always return a new promise, so it’s possible to chain promises with precise control over how and where errors are handled. Which can be done by chaining another then(at the end of the previous then() method. 

Example:

const p1 = new Promise((resolve, reject) => { 
	resolve(‘P1 is resolved’); 
}); 
const p2 = new Promise((resolve, reject) => { 
	resolve(‘P2 is resolved’); 
}); 
const p3 = new Promise((resolve, reject) => { 
	reject(‘P3 is rejected); 
}); 
p1 
.then((data) => { 
	console.log(data);   // P1 is Resolved 
	return p2; 
}) 
.then((data) => { 
	console.log(data);   // P2 is Resolved 
	return p3; 
}) 
.then((data) => { 
	console.log(data); 
}) 
.catch((error) => { 
	console.log(error);   //P3 is rejected 
});

Explanation

  • After the p1 is resolved, the then(method is invoked which returns p2.
  • After the p2 is resolved, the then(method is invoked which returns p3.
  • Now p3 is rejected so instead of next then(), catch() method is called which handle the rejection of the p3.

 

Tip: – When we put catch() method at the end of promise chain, catch() method is enough for handling rejections of any promise in the promise chain.

Have any question? for the above tip. Let’s see why we(developers) always end promise chain with catch().

Error Handling

As we already discussed that then(method takes two argument as parameter one for success and one for failure. 

Example 1:

promise.then( 
	handleSuccess, 
	handleError 
 );

What will happen if handleSuccess() throws an error?

The promise returned from then() will be rejected, we didn’t have any catch() block to catch that rejection and hence that error got swallowed.

Now let’s see what will happen if we use catch() block.

Example 2:

promise 
	.then(handleSuccess) 
	.catch(handleError) 
;
Note: – The difference between the two is fine, but important!

In the Example 1 an error message originated in the promise will be caught and the handleError() will be called. But, the error originated in the handleSuccess() will be swallowed.

Without-catch-block

But in Example 2 an error occurred either in the promise or in handleError() will be caught by the catch() block. 

With-Catch-block

Promise Methods

  1. Promise.all()

This method of the promise takes all the promises as an argument and return a brand-new promise. The returned promise is fulfilled if and only if all the promises inside the input array(argument) have fulfilled and the new promise gets rejected as soon as any one of the promises gets rejected. 

const promise1 = new Promise((resolve, reject) => { 
     setTimeout(() => { 
     resolve('Promise1 resolved'); 
    }, 2000); 
}); 

const promise2 = new Promise((resolve, reject) => { 
    setTimeout(() => { 
    resolve('Promise2 resolved'); 
    }, 1500); 
}); 

Promise.all([promise1, promise2]) 
  .then((data) => console.log(data[0], data[1])) 
  .catch((error) => console.log(error));

Let’s understand what is happening: –

  • The data attribute of the then(method is a type of array which contains the value of the promises in the same order in which they are passed to the Promise.all() method.

What will happen if we replace the resolve() method of the promise2 with the reject() method?

  • At first the promise1 get resolved after 2 seconds.
  • And the promise2 will get rejected after 1.5 seconds.

Now, as per the method definition, as soon as second promise gets rejected the returned promise from the method Promise.all() will also get rejected, without waiting for the promise1(others) to get resolved.

This method is used when we want to do some task only after all the promises get resolved.

Suppose we want to request different data from the different API’s and we want to do something with the requested data only if all promises are resolved successfully. 


2. Promise.race()

This method takes all the promises as an input argument and returns a new promise that gets resolved as soon as one of the promises of the input array gets resolved or fulfilled. The returned promise gets rejected as soon as one of the promises of the input array gets rejected. 

const promise1 = new Promise((resolve, reject) => { 
    setTimeout(() => { 
    resolve('Promise1 resolved'); 
}, 1000); 
}); 

 const promise2 = new Promise((resolve, reject) => { 
    setTimeout(() => { 
    reject('Promise2 rejected'); 
}, 1500); 
}); 

 Promise.race([promise1, promise2]) 
.then((data) => console.log(data))     // Promise1 resolved 
.catch((error) => console.log(error));

Let’s see what is happening: –

  • The first promise gets resolved after 1 seconds.
  • The second promise gets rejected after 1.5 seconds.

But the Promise.race() returned the resolved promise i.e. the returned promise gets resolved, as soon as the promise1 gets resolved without waiting for the others to get resolved(fulfilled) or rejected.

The data argument in the then(method holds the value of the resolved promise.

Conclusion

In this blog we have learned what is promises and how to create it along with how to consume it and learned how to use it in JS. We have also learned why we prefer promises over the events and the callback functions, how we can skip the problem of the callback hell.

That is, it and I hope you will find this blog useful!

Reference’s

  1. https://developers.google.com/web/fundamentals/primers/promises
  2. https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261
  3. https://www.geeksforgeeks.org/javascript-promises/
  4. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
  5. https://blog.bitsrc.io/understanding-promises-in-javascript-c5248de9ff8f

About The Author