Understanding ES6 Promises

Introduction

 
Promises are a clean way to implement async programming in JavaScript (ES6 new feature). Prior to promises, Callbacks were used to implement async programming. Let’s begin by understanding what async programming is and its implementation, using Callbacks. 
 

Understanding Callback

 
A function may be passed as a parameter to another function. This mechanism is termed a Callback. A Callback would be helpful in events.
 
The following example will help us better in understanding this concept.
  1. <script>    
  2. function notifyAll(fnSms, fnEmail)    
  3. {    
  4.     console.log('starting notification process');    
  5.     fnSms();    
  6.     fnEmail();    
  7. }    
  8. notifyAll(function()    
  9. {    
  10.     console.log("Sms send ..");    
  11. }, function()    
  12. {    
  13.     console.log("email send ..");    
  14. });    
  15. console.log("End of script"); //executes last or blocked by other methods    
  16. </script>   
In the notifyall() method, shown above, the notification happens by sending SMS and by sending an Email. Hence, the invoker of the notifyAll method has to pass two functions as parameters, each function is doing a single responsibility like sending SMS and sending an Email.
 
The output of the script would be,
 
starting notification process
Sms send ..
email send ..
End of script
 
In the code, mentioned above, the function calls are synchronous. It means the UI thread would be waiting to complete the entire notification process. Synchronous calls become blocking calls. Let's understand non-blocking or async calls now.
 

Understanding AsyncCallback

 
Consider the above example.
 
To enable the script, execute an asynchronous or a non-blocking call to notifyAll() method. We shall use the setTimeout() method of JavaScript. This method is async by default.
 
The setTimeout() method takes two parameters, 
  • A callback function and
  • The no. of seconds after which the method will be called.
In this case, the notifications process has been wrapped with timeout. Hence, it will take a two seconds delay, set by the code. The notifyAll() will be invoked and the main thread goes ahead like executing other methods. Hence, the notification process will not block the main JavaScript thread.
  1. <script>    
  2. function notifyAll(fnSms, fnEmail)    
  3. {    
  4.     setTimeout(function()    
  5.     {    
  6.         console.log('starting notification process');    
  7.         fnSms();    
  8.         fnEmail();    
  9.     }, 2000);    
  10. }    
  11. notifyAll(function()    
  12. {    
  13.     console.log("Sms send ..");    
  14. }, function()    
  15. {    
  16.     console.log("email send ..");    
  17. });    
  18. console.log("End of script"); //executes first or not blocked by others    
  19. </script>   
Output
 
End of script
starting notification process
Sms send ..
email send ..
 
In case of multiple callbacks, the code will look scary.
  1. <script>    
  2.     setTimeout(function()    
  3.     {    
  4.         console.log("one");    
  5.         setTimeout(function()    
  6.         {    
  7.             console.log("two");    
  8.             setTimeout(function()    
  9.             {    
  10.                 console.log("three");    
  11.             }, 1000);    
  12.         }, 1000);    
  13.     }, 1000);    
  14. </script>   
ES6 comes to your rescue by introducing the concept of promises. Promises are "Continuation events" and they help you to execute the multiple async operations together in a much cleaner code style.
 
Let's understand this with an example.
 
Syntax is given below:
  1. var promise = new Promise(function(resolve , reject){  
  2.    // do a thing, possibly async , then..  
  3.   
  4.    if(/*everthing turned out fine */)  
  5.    resolve("stuff worked");  
  6.   
  7. else  
  8.    reject(Error("It broke"));  
  9.   
  10. });  
  11.   
  12. return promise; // Give this to someone 
The first step towards implementing the promises is to create a method that will use the promise. Let’s say in this example, the getSum() method is asynchronous i.e. its operation should not block other methods’ execution. As soon as this operation completes, it will later notify the caller.
 
The below given example (Step 1) declares a Promise object ‘var promise’. The Promise Constructor takes to the functions first for the successful completion of the work and another in case an error happens.
 
The promise returns the result of the calculation by using the resolve callback and passing in the result i.e. n1+n2.
  1. resolve(n1 + n2);   
If the getSum() encounters an error or an unexpected condition, it will invoke the reject callback method in the Promise and pass the error information to the caller.
  1. reject(Error("Negatives not supported"));   
The method implementation is given below (STEP 1).
  1. function getSum(n1, n2)    
  2. {    
  3.     varisAnyNegative = function()    
  4.     {    
  5.         return n1 < 0 || n2 < 0;    
  6.     }    
  7.     var promise = new Promise(function(resolve, reject)    
  8.     {    
  9.         if (isAnyNegative())    
  10.         {    
  11.             reject(Error("Negatives not supported"));    
  12.         }    
  13.         resolve(n1 + n2);    
  14.     });    
  15.     return promise;    
  16. }   
The second step details the implementation of the caller (STEP 2).
 
The caller should use the ‘then’ method, which takes two callback methods- first for success and second for failure. Each method takes one parameter, as shown below:
  1. getSum(5, 6)    
  2. .then(function (result) {    
  3.    console.log(result);    
  4. },    
  5. function (error) {    
  6.    console.log(error);    
  7.     
  8. });   
Output
 
11
 
Since the return type of the getSum() is a Promise, we can actually have multiple ‘then’ statements. The first 'then' will have a return statement.
  1. getSum(5, 6)    
  2.     .then(function(result)    
  3.         {    
  4.             console.log(result);    
  5.             returngetSum(10, 20); // this returns another promise    
  6.         },    
  7.         function(error)    
  8.         {    
  9.             console.log(error);    
  10.         })    
  11.     .then(function(result)    
  12.     {    
  13.         console.log(result);    
  14.     }, function(error)    
  15.     {    
  16.         console.log(error);    
  17.     });   
Output
 
11
30
 
The example given below issues three then() calls with getSum() method. 
  1. <script>    
  2. function getSum(n1, n2)    
  3. {    
  4.     varisAnyNegative = function()    
  5.     {    
  6.         return n1 < 0 || n2 < 0;    
  7.     }    
  8.     var promise = new Promise(function(resolve, reject)    
  9.     {    
  10.         if (isAnyNegative())    
  11.         {    
  12.             reject(Error("Negatives not supported"));    
  13.         }    
  14.         resolve(n1 + n2);    
  15.     });    
  16.     return promise;    
  17. }    
  18. getSum(5, 6)    
  19.     .then(function(result)    
  20.         {    
  21.             console.log(result);    
  22.             returngetSum(10, 20); //this returns another Promise    
  23.         },    
  24.         function(error)    
  25.         {    
  26.             console.log(error);    
  27.         })    
  28.     .then(function(result)    
  29.     {    
  30.         console.log(result);    
  31.         returngetSum(30, 40); //this returns another Promise    
  32.     }, function(error)    
  33.     {    
  34.         console.log(error);    
  35.     })    
  36.     .then(function(result)    
  37.     {    
  38.         console.log(result);    
  39.     }, function(error)    
  40.     {    
  41.         console.log(error);    
  42.     });    
  43. console.log("End of script ");    
  44. </script>   
Output
 
The program displays ‘end of script’ first and then results from calling getSum() method, one by one.
 
End of script
11
30
70
 
This shows getSum() is called async style or non blocking style . Promise gives a nice and clean way to deal with the Callbacks.