Voice of a Developer: ECMAScript Promises - Part 32

JavaScript is a language of the Web. This series of articles will talk about my observations learned during my decade of software development experience with JavaScript.
 

Introduction

 
A Promise is an asynchronous operation that has not completed now but will complete in the future. ES6 has adopted promises implementation as native APIs. The promise gives us a guarantee to return the value in the future. Promises are not like events where you need to bind the event with a particular method.
 

Creating a promise

 
The way to create a Promise is by using "new Promise" constructor, which accepts two callback functions as parameters. The first typically named resolve is a function to call with the future value when promise is ready. The second typically named reject is a function to reject the Promise if it cannot resolve the future value.
 
Syntax
 
As per MDN, below is the syntax:
 
new Promise( /* executor */ function(resolve, reject) { ... } );
 
Executor: A function that will be passed to other functions via the arguments resolve and reject.
 
Sample code
  1. var p1 = new Promise(function(resolve, reject)    
  2. {    
  3.     if ( /* condition */ )    
  4.     {    
  5.         resolve( /* value */ ); // promise fulfilled     
  6.     }    
  7.     else    
  8.     {    
  9.         reject( /* reason */ ); // mention reason for rejection    
  10.     }    
  11. });  
Let us see what happens at Browser after we trigger the above code.
  • When code executes “new Promise”, it starts from below state
     
     
    The first state of Promise is “pending”.
     
  • After Promise is fulfilled then it changes to: 
     
     
    The second state of Promise is “fulfilled” or a promise is “rejected” if it’s not fulfilled.
     
     

Summary of Promise states

  • pending: Initial state, not fulfilled or rejected.
  • fulfilled: Meaning that the operation completed successfully.
  • rejected: Meaning that the operation failed.

Prototype promise methods

 
After a Promise is created, we need to pass around the value of this Promise. Inorder to consume a Promise value we attach a handler using via the below methods.
 
Two methods of Promise are associated with constructor prototype Promise.prototype these are:
 

then() method

 
It returns a promise with two arguments, i.e., onFulfilled, on Rejected
  • onFulfilled: a callback function called when the promise is fulfilled.
  • onRejected: a callback function is called when the promise is rejected.
Example- it will print “Fulfilled!” because we’ve set flag = true,
  1. var flag = true;    
  2. var p1 = new Promise(function(resolve, reject)    
  3. {    
  4.     if (flag)    
  5.     {    
  6.         resolve("Fulfilled!");    
  7.     }    
  8.     else    
  9.     {    
  10.         reject("Rejected!");    
  11.     }    
  12. });    
  13. p1.then(function(value)    
  14. {    
  15.     console.log(value); // Fulfilled!    
  16. }, function(reason)    
  17. {    
  18.     console.log(reason); // Rejected!    
  19. });   

catch() method

 
It returns a promise and deals with rejected cases only. If you pass null or undefined in .then() method first argument it .then() method behaves like .catch() method.
 
Example- Below code will print “Rejected!”
  1. var flag = false;    
  2. var p1 = new Promise(function(resolve, reject)    
  3. {    
  4.     if (flag)    
  5.     {    
  6.         resolve("Fulfilled!");    
  7.     }    
  8.     else    
  9.     {    
  10.         reject("Rejected!");    
  11.     }    
  12. });    
  13. p1.then(function(value)    
  14. {    
  15.     console.log(value); // Fulfilled!    
  16. }).catch(function(reason)    
  17. {    
  18.     console.log(reason); // Rejected!    
  19. });   
Example- Chaining promises via .then() method.
 
As .then(), .catch() methods return Promise so we could chain these [please refer Voice of a Developer JavaScript Chaining part 16 to understand Chaining].
 
Example- chaining of .then() method and incrementing counter.
  1. var counter = 0;    
  2. var p1 = new Promise(function(resolve, reject)    
  3. {    
  4.     resolve(counter);    
  5. });    
  6. p1.then((value) =>    
  7. {    
  8.     console.log('Counter: ' + ++counter); // 1    
  9. }).then((value) =>    
  10. {    
  11.     console.log('Counter: ' + ++counter); // 2    
  12. }).then((value) =>    
  13. {    
  14.     console.log('Counter: ' + ++counter); // 3     
  15. }).catch(function(reason)    
  16. {    
  17.     console.log(reason);    
  18. });   
Output
 
 

Promise methods

 
Promise.all()- When you are working with multiple promises, this function is really helpful. Once all Promises are resolved or rejected, it returns Promises.
  1. var p1 = new Promise(function(resolve, reject) {  
  2.  var counter = 0;  
  3.  resolve(++counter);  
  4. });  
  5. var p2 = new Promise(function(resolve, reject) {  
  6.  resolve("counter 2");  
  7. });  
  8. var p3 = new Promise(function(resolve, reject) {  
  9.  setTimeout(resolve("Promise 3"), 5000); //5000 milisecond = 5 sec    
  10. });  
  11. Promise.all([p1, p2, p3]).then(function(values) {  
  12.  console.log(values); // Output: [1, "counter 2", "Promise 3"]    
  13. }).catch((val) => {  
  14.  console.log(val); // return “rejected”, if any promise fails    
  15. }); 
Once all Promisesp1, p2, p3 are fulfilled.then() method is called. The above code .then()will execute after 5 seconds when all Promises are resolved (after setTimeout).
 
If any promise returns reject, then it will return “rejected”.
 
Promise.race()- It returns a Promise which resolves or reject first. It accepts an iterable of Promises and works like OR condition.
  1. var p1 = new Promise(function(resolve, reject)    
  2. {    
  3.     setTimeout(resolve, 1500, "resolved - will not get printed");    
  4. });    
  5. var p2 = new Promise(function(resolve, reject)    
  6. {    
  7.     setTimeout(reject, 100, "rejected - printed");    
  8. });    
  9. Promise.race([p1, p2]).then(function(value)    
  10. {    
  11.     console.log(value); // not get printed    
  12. }, function(reason)    
  13. {    
  14.     console.log(reason); // rejected - printed    
  15.     // p6 is faster, so it rejects    
  16. });   

Load XMLHttpRequestfiles

 
The use of Promises is by using XMLHttpRequestAPI asynchronously. It loads file in the background, example: it will load two JSON files via XHR requests:
 
automobile.json
  1. {    
  2.     "automobile": [    
  3.     {    
  4.         "vehicle""car",    
  5.         "engine""1200cc"    
  6.     },    
  7.     {    
  8.         "vehicle""bike",    
  9.         "engine""200cc"    
  10.     },    
  11.     {    
  12.         "vehicle""jeep",    
  13.         "engine""2000cc"    
  14.     }]    
  15. }   
employee.json
  1. {    
  2.     "employees": [    
  3.     {    
  4.         "firstName""John",    
  5.         "lastName""Doe"    
  6.     },    
  7.     {    
  8.         "firstName""Anna",    
  9.         "lastName""Smith"    
  10.     },    
  11.     {    
  12.         "firstName""Peter",    
  13.         "lastName""Jones"    
  14.     }]    
  15. }   
Script.js
  1. varary = ['http://localhost/automobile.json''http://localhost/employee.json']    
  2. var p1 = new Promise(function(resolve, reject)    
  3. {    
  4.     letsrcurl = getJSON(ary[0], resolve);    
  5. });    
  6. var p2 = new Promise(function(resolve, reject)    
  7. {    
  8.     letjson = getJSON(ary[1], resolve);    
  9. });    
  10. functiongetJSON(json, resolve)    
  11. {    
  12.     varxmlhttp = new XMLHttpRequest(json);    
  13.     xmlhttp.open("GET", json);    
  14.     xmlhttp.send();    
  15.     xmlhttp.onload = function()    
  16.     {    
  17.         if (xmlhttp.status === 200)    
  18.         {    
  19.             resolve(xmlhttp.responseText);    
  20.         }    
  21.     }    
  22. };    
  23. Promise.all([p1, p2]).then((val) =>    
  24. {    
  25.     document.getElementById('div2').innerHTML = val;    
  26. });   
Output: it will load data of ‘val’ into ‘div2’ of HTML.
 
 

Promises Advantages

  • It gives us the ability to write async code synchronously.
  • You can handle via handler whether it's resolved or rejected
  • Solve the problem of code pyramids, ex-
    1. step1(function(value1)    
    2. {    
    3.     step2(value1, function(value2)    
    4.     {    
    5.         step3(value2, function(value3)    
    6.         {    
    7.             step4(value3, function(value4)    
    8.             {    
    9.                 // Do something with value4    
    10.             });    
    11.         });    
    12.     });    
    13. });   
This is solved and simplified via Promise prototypal methods & chaining.
  1. var p1 = new Promise().resolve('resolve');    
  2. p1.then((value) =>    
  3. {    
  4.     console.log(value);    
  5. }).then((value) =>    
  6. {    
  7.     console.log(value);    
  8. }).then((value) =>    
  9. {    
  10.     console.log(value);    
  11. }).then((value) =>    
  12. {    
  13.     console.log(value);    
  14. });   

Libraries providing Promises

 
Promises are supported via different libraries Q, AngularJS, BlueBird, etc. There are many libraries providing promises as an abstraction on top of JavaScript Native Promises API, e.g.
 
Q.js
  1. var functionA = function()    
  2. {    
  3.     return "ret A";    
  4. };    
  5. var functionB = function()    
  6. {    
  7.     return "ret B";    
  8. };    
  9. var promise = Q.all([Q.fcall(functionA), Q.fcall(functionB)]);    
  10. promise.spread(finalStep);   
AngularJS
  1. // Simple GET request example:    
  2. $http(    
  3. {    
  4.     method: 'GET',    
  5.     url: '/someUrl'    
  6. }).then(function successCallback(response)    
  7. {    
  8.     // this callback will be called asynchronously    
  9.     // when the response is available    
  10. }, function errorCallback(response)    
  11. {    
  12.     // called asynchronously if an error occurs    
  13.     // or server returns response with an error status.    
  14. });   
It generates an HTTP request and returns a response.
 

Summary

 
Please share your feedback/comments. 
 
<< Voice of a Developer: JavaScript Web App Performance Best Practices - Part Thirty-One


Similar Articles