Angular.js Promise

Promises are a core feature of AngularJS.A promise implementation inspired by Kris Kowal’s Q.A promise represents the eventual result of an operation. We can use a promise to specify what to do when an operation eventually succeeds or fails.

The CommonJS Promise describes a promise as an interface for interacting with an object. Which represents the result of an action that is performed asynchronously. It does not guarantee any fixed response time. From the perspective of dealing with error handling, deferred and promise APIs are to asynchronous programming what try, catch and throw keywords are to synchronous programming.

So let’s see this in action. Look at the code below:

$http.get("/api/my/name");

This code uses the $http service to perform an HTTP GET on the url ‘/api/my/name’. Let’s say that this is an api we’ve implemented on our server that returns the name of the logged in user.

The Deferred API
A new instance of deferred is constructed by calling $q.defer(). The purpose of the deferred object is to expose the associated Promise instance as well as APIs that can be used for signaling the successful or unsuccessful completion, as well as the status of the task.

Methods

resolve(value) – resolves the derived promise with the value.
reject(reason) – rejects the derived promise with the reason.Rejected via $q.reject.
notify(value) – provides updates on the status of the promise’s execution. This may be called multiple times before the promise is either resolved or rejected.

Properties

promise – {Promise} – promise object associated with this deferred.

The Promise API
A new promise instance is created when a deferred instance is created and can be retrieved by calling deferred.promise.The purpose of the promise object is to allow for interested parties to get access to the result of the deferred task when it completes.

Methods

then(successCallback, errorCallback, notifyCallback) – The callbacks are called with a single argument: the result or rejection reason.The notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback. It also notifies via the return value of the notifyCallback method. The promise can not be resolved or rejected from the notifyCallback method.

catch(errorCallback) – shorthand for promise.then(null, errorCallback)

finally(callback) – To observe either the fulfillment or rejection of a promise,without modifying the final value. This is useful to release resources or do some clean-up that needs to be done. Because finally is a reserved word in JavaScript and reserved keywords are not supported as property names by ES3.

Differences between Kris Kowal’s Q and $q:

There are two main differences:

$q is integrated with the $rootScope.Scope. Which leads to faster propagation of resolution or rejection into models and avoiding unnecessary browser repaints, which would result in flickering UI.
Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains all the important functionality needed for common a sync tasks.

Now, as we got a general idea about promise in the above. It is time to reveal that idea in a sample code. Below we are going to see a process for retrieving a certain amount of data from server and cache. We will be hard-coding the info in this sample code. But in real life the info will be retrieved from server or cache respectively.

To achieve this task we have to use data binding in html for viewing the desired result. And a certain hyperlink “Update Name” to fire the ng-click method. Let us see the codes:

<div ng-app="app">
    <div ng-controller="MainController">
        <h1> Phloxblog promise sample</h1>
        <p><strong>Name:</strong> {{name}}</p> <a href ng-click="updateName()">Update Name</a>

    </div>
</div>

Below is the code for js file:

var fiddleResponse = 'json=' + encodeURIComponent(angular.toJson({
    name: "Phloxblog"
}));
var fiddleHeaders = {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
};

var app = angular.module('app', []);

app.factory('NameService', function ($http, $q) {

    //    Create a class that represents our name service.
    function NameService() {

        var self = this;

        self.name = null;

        //    getName returns a promise which when fulfilled returns the name.
        self.getName = function () {

            //    Create a deferred operation.
            var deferred = $q.defer();

            //    If we already have the name, we can resolve the promise.
            if (self.name !== null) {
                deferred.resolve(self.name + " (from Cache!)");
            } else {
                //    Get the name from the server.
                $http.post('/echo/json/', fiddleResponse, fiddleHeaders)
                    .success(function (response) {
                    self.name = response.name;
                    deferred.resolve(response.name + " (from Server!)");
                })
                    .error(function (response) {
                    deferred.reject(response);
                });
            }

            //    Now return the promise.
            return deferred.promise;
        };
    }

    return new NameService();
});

app.controller('MainController', function ($scope, NameService) {

    //    We have a name on the code, but it's initially empty...
    $scope.status = "Active";
    $scope.updateName = function () {
        NameService.getName()
            .then(
        /* success function */
        function (name) {
            $scope.name = name;
        },
        /* error function */
        function (result) {
            console.log("Failed to get the name, result is " + result);
        });
    };
});

Sample for data retrieving from server.

promise

Sample for data retrieving from cache.

promisecache

Chaining promises:

Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs like $http’s response interceptors.
One useful aspect of promises is that the then function returns the promise itself. This means that you can actually chain promises, to create concise blocks of logic that are executed at the appropriate times, without lots of nesting.

Let us see a basic javascript file to know how the promise chaining works.

angular.module('WebShopApp')
  .controller('CheckoutCtrl', function($scope, $log, CustomerService, CartService, CheckoutService) {

    function calculateTotals(cart) {
      cart.total = cart.products.reduce(function(prev, current) {
        return prev.price + current.price;
      };

      return cart;
    }

    CustomerService.getCustomer(currentCustomer)
      .then(CartService.getCart) // getCart() needs a customer object, returns a cart
      .then(calculateTotals)
      .then(CheckoutService.createCheckout) // createCheckout() needs a cart object, returns a checkout object
      .then(function(checkout) {
        $scope.checkout = checkout;
      })
      .catch($log.error)

    });

So, we have a promise chaining above. Now to understand it’s working procedure.This combines getting data asynchronously (customers, carts, creating a checkout) with processing data synchronously (calculateTotals); the implementation however doesn’t know or need to know whether those various services are a sync or not, it will just wait for the methods to complete, a sync or not. In this case, getCart() could fetch data from local storage, createCheckout() could perform a HTTP request to make sure the products are all in stock, etc. But from the consumer’s point of view (the one making the calls), it doesn’t matter; it Just Works. And it clearly states what it’s doing, just as long as the result of the previous then() is passed to the next.And it’s self-documenting and concise.

Advanced Promises – Routing:
There’s a particular area of AngularJS that uses promises to great effect, and that’s the router. With the help of $routeProvider we can retrieve data from either server or from cache in same page. Let us go through the code to do it.

var fiddleResponse = 'json=' + encodeURIComponent(angular.toJson({
    name: "User"
}));
var fiddleHeaders = {
    headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        }
};

var app = angular.module('app', []);

app.config(function($routeProvider) {

    var getName = function(NameService) {
        return NameService.getName();
    };

    $routeProvider
   .when('/home', {
       templateUrl: '/home.html',
       controller: 'MainController'
   })
   .when('/profile', {
       templateUrl: '/profile.html',
       controller: 'ProfileController',
       resolve: {
           name: getName
       }
   })
   .otherwise({redirectTo: '/home'});
});

app.factory('NameService', function($http, $q) {

    //    Create a class that represents our name service.
    function NameService() {

        var self = this;

        //    Initially the name is unknown....
        self.name = null;

        //    getName returns a promise which when fulfilled returns the name.
        self.getName = function() {

            //    Create a deferred operation.
            var deferred = $q.defer();

            //    If we already have the name, we can resolve the promise.
            if(self.name !== null) {
                deferred.resolve(self.name + " (from Cache!)");
            } else {
                //    Get the name from the server.
                $http.post('/echo/json/', fiddleResponse, fiddleHeaders)
                .success(function(response) {
                    self.name = response.name;
                    deferred.resolve(response.name + " (from Server!)");
                })
                .error(function(response) {
                    deferred.reject(response);
                });
            }

            //    Now return the promise.
            return deferred.promise;
        };
    }

    return new NameService();
});

app.controller('MainController', function ($scope, NameService) {

    //    We have a name on the code, but it's initially empty...
    $scope.name = "";

    //    We have a function on the scope that can update the name.
    $scope.updateName = function() {
        NameService.getName()
            .then(
                /* success function */
                function(name) {
                    $scope.name = name;
                },
                /* error function */
                function(result) {
                    console.log("Failed to get the name, result is " + result); 
            });
    };
});

app.controller('ProfileController', function($scope, name) {

    $scope.name = name;
});

In the above we have seen one of the beautiful features of angular.js. That is Promise. We have discussed about this feature and it’s implementation throughout the angular.js. Though promise is a code part and it’s developing everyday. We can assume that in near future we can have many other implementation of this feature.

Related Links:
1>Directive in Angular.js – Part 1
2>Directive in Angular.js – Part 2
3>Working with Modules in Angular.js
4>Angular Router and UI-Router
5>Angularjs and Services
6>Angular.js Two Way Data Binding
7>How To Use Filter In Angular.js
8>Angular Templates
9>Using Controllers in Angular.Js
10>Angularjs with Server Side Interaction
11>Working Through Angular.js With Transclude
12>Angular.js Event Handling

If you find this article helpful, you can connect us in Google+ and Twitter.