Sysleaf

What is ES6 promise in Javascript, when & how to use it

. .
What is ES6 promise in Javascript, when & how to use it

Introduction

Promise brings the paradigm shift in asynchronous programming. Before promise, we used to write lots of async code with callback and be the victim, sometimes, of callback hell. Callback hell makes our code difficult to read and hard to maintain which makes it easy to introduce bug or error in our code. Thanks to promise, it does not only alleviate such problem but encourage us to write more async code.

To use promise, before it could come natively in Javascript, we used to be dependent on a various external library like Q, When, Bluebird etc. Each of these promise libraries had a different implementation with a slight deviation from standard behavior. But now, promise has lately(es6[ES2015]) but natively arrived in javascript with standard behavior. In this article, we will learn about the promise which is part of standard Javascript.

As of today, Promise is supported in all the major browser except Internet Explorer.

To demonstrate the point in a concise way, you might see only the partial code in between the article. In the 'reference section', you can get all the links to complete source code, live demo and external references, if any.

Conceptualization: example

Remember: we should not just know how we are implementing things; we should also know, what things! we are implementing. So, it is very important to first understand, what is promise, before learning how to implement this.

Promise in Javascript is very much like the promise in the real world. So we will take a real world example and will try to characterize it. Do you know, what do we mean by characterization? Well! every object in the real world has two characteristics: 1. Behavior and 2. State. So here, we will try to find the behavior and state of real world promise.

Example: “Suppose, you made a promise to me that you will go to the market and bring a pen for me.” Now let’s characterize your promise as shown below.

Behavior: going to market and bringing the pen for me.

State:

  1. Fulfilled: if you returned from market and brought the pen for me.
  2. Broken: if you returned from the market but did not bring the pen for me.
  3. Pending: you have not come yet from market.

It’s same three states of promise that exist in Javascript also, but you might hear a different name for the same state. For example, we also call ‘resolved’ for ‘fulfilled’ and ‘rejected’ for ‘broken’.

Sometimes, you will also read/hear about the 4th state of promise, called ‘settled’. ‘settled’ is a state when the promise has either resolved or rejected. So technically, it’s not a separate state, instead it is one of ‘resolved’ or ‘rejected’.

Implementation: syntax

So in above section, we have conceptualize the behavior and state of promise. In this section, we will learn how to implement these behavior & state of promise in Javscript. While doing this, we also learn some basic promise syntax in Javascript.

How to create?

In Javascript, promise is actually an object, so if you know how to create object in Javscript then you would easly know how to create promise. So 1st step to create promise object is to use new operator with its function constructor Promise() like new Promise(). See below example ‘Create: Step1’.

So now the 2nd step: from our example in the introduction section, we know that promise is a behavior with one of the states from ‘resolve’, ‘reject’, or ‘pending’. And behavior is nothing but function, work, or operation. So let’s define the behavior of our promise. See how to do this in below example ‘Create: Step2’.

Notice how we are passing two arguments, namely ‘resolve’ and ‘reject’, in function definition of myPromiseBehavior. We can give any other name to these argument, but we generally keep these name to align with promise concept.

  • var myPromise = new Promise();
    
  • var myPromiseBehavior = function(resolve, reject){
      //do some work here
      var data = getSomeData();
      if(data){
        //resolve your promise when you think its success
        resolve(data);
      }else{
        //reject your promise when you think its error
        reject('could not get the data');
      }
    }
    
    var myPromise = new Promise(myPromiseBehavior);
    

See also, how we are deciding the state of our promise? When we think that function has succeeded in its work or purpose then we put that promise in a resolved state by calling a resolve() function. Similarly, if we think that promise has failed in its work or purpose then we put that promise in a rejected state by calling a reject() function. If we don’t call either resolve or reject function, then this promise will always be in pending state.

So promise in Javascript is an object with such a behavior whose state will always be in one of resolved, reject, or pending state.

How to use?

So now we know how to create promise but how do we use it? What do we mean by use? Let’s take an example in the real world. Suppose you have made me a promise, now if you break promise: I will cry; if you fulfill the promise: I will smile. So here, depending on your promise state(break or fulfill), I want to do some work(cry or smile). If I don’t cry or smile then there is no use of your promise.

Similarly in Javascript, using promise means, calling some function once the promise is settled. Let’s see how to do this in below example, but before this, let us clarify our requirement. The requirement is simple, we want to call function onSuccess if the promise is resolved and onError if the promise is rejected.

//To use promise, just call its 'then' method
myPromise.then(onSuccess, onError);
  //OR
myPromise.then(onSuccess).catch(onError);


function onSucces(){
  console.log("promise has fulfilled");
}

function onError(){
  console.log("promise has rejected");
}

So from above code, it seems that there are two ways to use promise. Yes! especially for handling reject(error) state. Promise object has two method then and catch. These methods are building blocks for usage of promse. then method takes two arguments as a functtion. 1st arugment is handler function for resolved state and 2nd argument is handler function for reject state. If we don’t pass 2nd argument in then() method then it call catch method, catch method takes one argument which is handler function for reject state.

So to use promise, we utilize its ‘then’ & ‘catch’ method. ‘then’ method takes two handler function as arguments for ‘resolve’ & ‘reject’ state respectively. ‘cath’ method is used to pass reject handler.

Promisify: actual promise’s use

Whatever we learned in above section is just the basic concept and syntax of promise. We didn’t learned why & where exactly we use promise?

When we call any function, we are mostly interested in whether that function failed or succeeded in its purpose. Depending on its ‘failed’ or ‘succedded’ status, we call some other function. Promise also has two states resolved and rejected which is very similar to function’s ‘failed’ or ‘succeeded’ status. We can apply this promise’s state to function’s behavior.

To do this, we simply have to return a promise object from function and wrap all function’s logic inside that promise object. Making any function to return promise is called Promisifying the function. Any function in Javascript can be promisified but we should only promisify asynchronous function because that is where the power of promise lies.

How to promisify function?

By wrapping all the function logic inside promise constructor. See the example below, but before that, let us clarify our requirement. The requirement is simple: we have a function called getMyFavouriteDigit, which return my favourite digit(6) asynchronously and we want to promisify this function.

  • function getMyFavouriteDigit() {
      var promiseBehavior = function (resolve, reject) {
        //wrap your all function code here
        //if success, call resolve with success data
        //if error, call reject with error data
      }
    
      return new Promise(promiseBehavior);
    }
    
  • function getMyFavouriteDigit() {
      var promiseBehavior = function (resolve, reject) {
        setTimeout(() => {
          var randomDigit = Math.floor(Math.random()*9);
          if (randomDigit === 6) {
            resolve(randomDigit);
          } else {
            reject(randomDigit);
          }
        }, 1000);
      }
    
      return new Promise(promiseBehavior);
    }
    

See above, how function’s logic is wrapped inside promise constructor and how the function is returning promise object.

How to use promisified function?

Since promisified function return promise object, we can use it like a normal promise. Using promise means calling some other dependent function once the promise has settled. For example, we want to call function printMyFavouriteDigit after getMyFavouriteDigit function has resolved its promise. See how to do this in below code. See, how we are utilizing promise’s then method.

function printMyFavouriteDigit(digit){
  console.log('I got my favourite digit ' + digit );
}

getMyFavouriteDigit.then(printMyFavouriteDigit);

Chaining promises

Promise has some very interesting usage pattern: one of them is called ‘chaining of Promises’. We have to take help of Promise’s chain, when we come into a situation, where we have multiple interdependent async functions and each of these function should run one after another.

Let’s take an example: suppose function funcGetText2 is dependent on the result(text1) of function funcGetText1. Similarly funcGetText3 is dependent on funcGetText2 and printFinalText is dependent on funcGetText3 and of course each function is promisified function. Basically, each function should be called only when its previous function has finished.

See below code on how to use a chain of promise for such scenario, you can also see how to implement the same using callback to just have an idea. Notice, how we are making a chain of then method by passing each function in required order.

  • funcGetText1(function(text1){
      funcGetText2(text1, function(text2){
        funcGetText3(text2, function(text3){
          printFinalText(text3);
        })
      })
    });
    
  • funcGetText1()
      .then(funcGetText2)
      .then(funcGetText3)
      .then(printFinalText)
    

Array of promises

Another interesting usage pattern of promise is the ‘array of promises’. we use this pattern when we have a situation, where we have multiple independent async functions and all these functions have to run parallelly.

Let’s take an example: suppose we want to fetch four URLs at the same time. Fetching of one URL is not dependent on another url, so it has to be done parallelly. We also want to print the result of each URL fetch only when all of the URLs have been fetched. See below code on how to do this.

Assume ‘fetch’ function in below code returns promise. So first we are creating an array of promise using promiseArray.push() then we are passing this array to all() method of promise object. Promise.all() ensure that any handler in then or catch method is called only when all the promise in promisArray is settled.

var promiseArray = [];
var urls = ['url1', 'url2', 'url3', 'url4'];

for(var i=0; i<; i++){
  promiseArray.push(fetch(urls[i]));
}

Promise
  .all(promisArray)
  .then(printResult)
  .catch(errorHandler)

function printResult(result){
  console.log("all urls has been fetched");
  for(var i=0; i<result.length; i++){
    console.log("result for url ", i, " is ", result[i]);
  }
}

function errorHandler(err) {
  console.log('some error occured while fetching urls: ',err);
}

Please note the result variable in above code. Variable result is an array which contains the content of each URL fetch. Number of element in result will always be equal to a number of element in promiseArray. Also to be noted is that when any of these promises is rejected then whole Promise.all() is rejected, resulting in an error condition. When Promise.all() is rejected, it directly goes to catch() method, skipping then() method. In such situation, we should check which all promise is rejected and why in errorHandler.

Error handling

Whenever promise is rejected, it is treated as an error and we must provide error handler otherwise it will crash the application. There are two way to handle promise error. one way is to use then method and another way is to catch method. As discussed in intro section, we pass our error handler as second argument in then method.

Let’s take an example: suppose function getMyFavouriteDigit is rejecting its promise when it is not able to return my favourite digit 6. See how to handle this in below example.

  • function getMyFavouriteDigit() {
      var promiseBehavior = function (resolve, reject) {
        setTimeout(() => {
          var randomDigit = Math.floor(Math.random()*9);
          if (randomDigit === 6) {
            resolve(randomDigit);
          } else {
            reject(randomDigit);
          }
        }, 1000);
      }
    
      return new Promise(promiseBehavior);
    }
    
    function printMyFavouriteDigit(digit){
      console.log('I got my favourite digit ' + digit );
    }
    
    function errorHandler(digit){
      console.log('I could not get my favourite digit 6 but ' + digit );
    }
    
  • getMyFavouriteDigit
      .then(printMyFavouriteDigit, errorHandler);
    
  • getMyFavouriteDigit
      .then(printMyFavouriteDigit)
      .catch(errorHandler)
    

Now let’s take another short example to understand the flow of error handling in a chain of promise. In below code, errorHandler1 will be called when any of funcGetText1, funcGetText2 and funcGetText3 reject its promise. While errorHandler2 will be called when any of errorHandler1, funcGetText4 and printFinalText reject its promise.

funcGetText1()
  .then(funcGetText2)
  .then(funcGetText3)
  .catch(errorHandler1)
  .then(funcGetText4)
  .then(printFinalText)
  .catch(errorHanlder2)

So error handling in Promise’s chain is just like try/catch. Any error in Promise’s chain goes to its immediate catch method in promise chain.

vs callback

As we know, in javascript, we can do async stuff using promise or/and callback. So is there any advantage of one over other? Well! that is what we will see in this section with the help of small example.

Seperation of Concern: Ideally, when we call any function, we should only care about its result and depending on its result, call any other dependent function. But with the callback, it is not possible. In the callback, caller function need to know about dependent function in advance and caller function decide when to call the dependent function.

For example: in below code ‘Async: Using Callback’, getMyFavouriteDigit function need to know about printMyFavouriteDigit function in advance and function ‘getMyFavouriteDigit’ decide when to call function ‘printMyFavouriteDigit’.

To maintain separation of concern: one function needs to know about its own business, not other’s; otherwise, there will be a conflict of interest. Thanks to promise, now each function has their own concern only, dependency is linked at the time or wiring.

  • function getMyFavouriteDigit(fnCallback) {
        setTimeout(()=>{
          var randomDigit = Math.floor(Math.random() * (9 - 1)) + 1;
          if(randomDigit === 6){
            //see here, need to know about dependent function
            fnCallback(randomDigit)
          }
        }, 2000);
     }
    
    function printMyFavouriteDigit(digit){
      console.log('I got my favourite digit ' + digit );
    }
    
    //See here, need to pass dependent function as argument.
    getMyFavouriteDigit(printMyFavouriteDigit);
    
  • //no need to know about dependent function
    function getMyFavouriteDigit() {
      return new Promise((fulfill, reject) => {
        setTimeout(()=>{
          var randomDigit = Math.floor(Math.random() * (9 - 1)) + 1;
          if(randomDigit === 6){
            fulfill(randomDigit);
          }
        }, 2000);
      });
     }
    
    function printMyFavouriteDigit(digit){
      console.log('I got my favourite digit ' + digit );
    }
    
    //no need to pass dependent function as argument
    getMyFavouriteDigit()
      .then(printMyFavouriteDigit);
    

Callback Hell: with too much usage of callback, we come into a siutation called callback hell that make our code hard to read and difficult to maintain. Thanks to promise, we can avoid this situation.

On the day of writing this article, there is one new alternative called async await which is better than callback and promise.

Reference

  1. See basic of promises on google web fundamentals
  2. See promise api on MDN