Promise API($q) explained in angularJS

Promise API may sound like an unncessary complexity but it helps a great deal in making the chaining of function calls and handling exceptions bearable . While, these things seem pretty easy in the synchronous world, it becomes very perplexing in the asynchronous javascript world.

AngularJS comes with the $q (Pomise API) , which is lightweight . Many Angular services like $http, $timeout and others heavily rely on promise style APIs (they return a promise, by default) , so it is a good idea to get familiar with $q in order to use those services effectively , and in order to manage your numerous asynchronous events .

 In case you are confused what a promise actually means , you can understand it from an example : Suppose , you ask your friend to bring a Pink Floyd T-shirt from a store . Now, if your friend promises you that he will bring you one , then its a promise (quite obvious :P ) . There are two scenarios now, either he can fulfill the promise or he cannot . Make sure you understand that the task you gave to your friend is asynchronous, you can do other tasks while he tries to fulfill the promise . Again, if he is successful in fulfilling the promise, there can be other outcomes like 'If you liked the T-shirt' ,'If T-shirt was not of your size' etc (we can chain these events).

Lets take this example , and try to understand the $q API and its various functionalities .

lets create a person constructor and pass name and $log service as arguments to it .
( name refers to the name of the person who asked for the T-shirt). we will add methods to be called during the success or failure of the promise here.
var Person=function(name,$log){
this.gottshirt=function(value){
$log.info(name+ "got his t-shirt" + value);
};
this.didntget=function(reason){
$log.warn(name+ "didnt get the t-shirt because" + reason );
};
} ;


Now, we will create various scenarios as Jasmine Tests to illustrate the usage of $q .Jasmine is a  behaviour -driven development framework for testing Javascript code.

Lets, see the basic usage first :

First, inject the various dependencies , to be assigned before every test begins. beforeEach is a function which is used to inject dependencies and create variables and objects , which are mandatory for every running instance of the test.
beforeEach(inject(function(_$q_,_$log_,_$rootScope_){
$q=_$q_;
$log=_$log_;
$rootScope=_$rootScope_;
aakash= new Person('Aakash',$log);
}));


 In the beforeEach function, I am creating a new user Aakash , who ordered his friend for a T-shirt ,
 now , his friend is basically a service which is used by Aakash .

Now lets start a test suite. A test suite begins with a call to the global Jasmine function 'describe' with two parameters: a string and a function . The string is a name or title for a spec suite(like what test we are running). The function is a block of code that implements the suite.
describe ('$q used in a service ' , function(){
var friend=function($q,$rootScope){
var deferred;
this.goingtobuy=function(){
deferred=$q.defer();
return deferred.promise;
}
/* here , the call to the $q.defer() method  returns a deferred
object. Conceptually it represents a task that will be completed (or will fail in the
future). The deferred object is used for two things
     1) It holds a promise object (in the promise //property). Promises are placeholders for the future //results (success or failure) of a deferred task.
 2)It exposes methods to trigger future task completion ( resolve ) or failure (reject)*/
this.has_bought=function(){
deferred.resolve();
$rootScope.$digest;
}
 //the resolve method triggers the completion of task .
this.notfound=function(reason){
deferred.reject(reason);
$rootScope.$digest;
}
//the reject method triggers the failure of task.
});
// here i am creating an instance of a friend called  thomas, before each test suite starts.
      var Thomas;
      beforeEach(function () {
        Thomas = new friend($q, $rootScope);
      });


 'it' is a jasmine function which runs each spec or test . Here, we are running a test for the case when the promise is not fulfilled.


    it('should illustrate promise failure', function () {
/*here i am creating a deferred object 'shirtOrdered' . the 'then' method registers callbacks if the task is successful or promise is fulfilled (as the first argument) , and if task is failed(as the second argument).*/
        var shirtOrdered = Thomas.goingtobuy();
        shirtOrdered.then(aakash.gottshirt, aakash.didntget);
/* we have triggered task failure (notfound method). Expect function calling is quite verbose , like in this case, it tells to expect $log.warn.logs to contain the given string.*/
        Thomas.notfound('No Pink Floyd Tshirts, sold out ');
        expect($log.warn.logs).toContain(['Aakash didnt get the t-shirt because No Pink Floyd Tshirts, sold out']);
      });
// this test is for the case of promise fulfilment.
      it('should illustrate promise success', function () {
        var shirtOrdered = Thomas.goingtobuy();
        shirtOrdered.then(aakash.gottshirt, aakash.didntget);
        Thomas.has_bought();
        expect($log.info.logs).toContain(['Aakash got his T-shirt']);
      });
});


Hope, this post gives you some insight of the promise api in angularJS .  Do tell me if i am wrong anywhere .

3 comments:

  1. Thanks for great information on angularjs. My request to continue posting the information on the subject as I am a regular learner from this blog. Our instructors recommended this blot at online Angularjs training

    ReplyDelete
  2. Thanks a lot . you have given enough inspiration to keep blogging :)

    ReplyDelete