Promise Sharing in AngularJS

The Problem

As any software project's complexity increases, separation of concerns becomes more and more important for overall maintainability. Angular services are a great way for multiple controllers to share functionality, and promotes the kind of decoupling that leads to clean, maintainable code. Although Angular services can really do anything, one of the more common uses of Angular services is to reach out to your server (via $http) and get some data. This is a great way to separate data-fetching logic from the business logic of your web application. However, this can also be a problem as your application becomes large.

Let's say your application has multiple subviews, each with its own controller. These controllers know nothing about each other (since you're a good architect), and each has a dependency on the same Angular service to fetch some data from the same API endpoint. This results in two service calls to the server, each requesting the same data. For small requests, this doesn't seem like a big deal, but it could be an issue for longer requests. This could also be an issue if you're navigating to a new view, and that new view's controller issues a request for the same resource (before the previous request has even returned). Web browsers can only maintain a small number of connections to a server at a time, and so this could lead to some latency issues. 

The Solution

When a service sends a request out to the server, it usually returns a promise object that will resolve when that web request returns data. Normally, this would mean there'd be one promise per call to the Angular service. This is actually a bit inefficient. If there's already been a promise given out by the service for a specific HTTP route that hasn't been resolved yet, why would we then queue up another one? We should simply return the same promise object! That way, the second controller doesn't have to wait as long for the request to return, since it's already being processed. The trick is simply to keep track of the "currently issued promises", so that this angular service can return the appropriate promises. Remember, Angular services are singletons, so it's the same service in each and every controller with a dependency on it. 

So, what do we need to do to set something like this up? Well, firstly, we'd need a wrapper around $http.


    
var Services;
(function (Services) {
    var AjaxService = (function () {
        function AjaxService($http, $q) {
            this.activePromises = {};
            this.$q = $q;
            this.$http = $http;
        };
        AjaxService.prototype.http_get = function (route) {
            var _this = this;
            var deferred = this.$q.defer();
            var origin = location.protocol + '//' + location.host;
            if (this.activePromises[route] != null)
                return this.activePromises[route];
            this.$http.get(origin + "/api/" + route).success(function (response) {
                deferred.resolve(response);
            }).error(function (response) {
                deferred.reject(response);
            }).finally(function () {
                _this.activePromises[route] = null;
            });
            this.activePromises[route] = deferred.promise;
            return this.activePromises[route];
        };
  
        AjaxService.prototype.http_post = function (route, date) {
            // logic for http post
        });
  
        // http_patch or http_delete function wrappers here.
    })();
    Services.AjaxService = AjaxService;
})(Services || (Services = {}));

So what's going on here? The AjaxService service is simply a wrapper around $http. It also maintains a hash table of all currently issued promises ("activePromises"). Again, this service is being used by multiple controllers and directives throughout your application. Whenever http_get is called, the AjaxService first checks to see if some other controller or directive has issued a request to that route by checking it's "activePromises" hash table. If not, then it will issue the request normally, and add the returned promise to it's activePromises table. If some other controller calls this same route BEFORE the first promise has resolved, that controller will receive the same promise. It won't issue another request to the server, since there's already one "in progress". Once that promise has been resolved, the promise is removed from the activePromises table, and the next request will make a new request to the server.

Multiple controllers can share promises this way, and help optimize your web application.

Comments

  1. Thank You for Sharing your words. Mobile marketing is quite popular among companies as it gives maximum return on investment. Another good thing about this advertising is that it is relatively cheaper than traditional forms of marketing. You searchbulk sms service in coimbatore there are a lot of companies that provide this service. They spend millions of dollars in advertising and they won't hesitate in investing a few hundred dollars in SMS marketing if they find merit in it. The reality is that mobile advertising is beneficial for every business whether it is a manufacturing unit or a service group. In short, you won't have to struggle to get customers for your reseller work as clients will come to you as soon as they come to know about your SMS reseller service.

    Bulk SMS Service Coimbatore |
    OTP SMS Coimbatore | Service Implicit Coimbatore

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete

Post a Comment

Popular posts from this blog

Communicating with your iOS app over USB (C# and/or Xamarin)

Creating a PayPal IPN Web API Endpoint

Strategy Pattern: Switch Statements, Begone!