Home > Software Development > How to Wait for Promises in AngularJS

How to Wait for Promises in AngularJS

In one of my previous blogs – How to Cache Results from Asynchronous Calls in AngularJS, I mentioned a technique that downloads data from remote server via $http service as a promise. This is quite often used in Web application development, for example, you want to save configuration, user preference on remote server and download them on fly.

The previous technique works well, but it may feel a bit strange with all the promise syntax there. After writing the blog, I came to know another technique that can further simplify the experience using the built-in ngRoute.

Time to learn how to "Google" and manage your VMware and clouds in a fast and secure

HTML5 App

Download one URL

Let’s take a look at a concrete example that illustrate the usage. It relies on the $routeProvider which resolves all the promises in the resolve part. In our case, it would wait for the LoadService.loadPromise to finish. As you would expect, the loadPromise simply load from a URL via HTTP and assign the value to local variable. Notice the difference of self and this here. If you use “this.data1 = data”, it will not work because with the inner function, the “this” is not the “this” for the service. To solve this, use a variable to pass reference into the inner function.

Once the promise is done, the ngRoute will render the page based on the template and controller. From the controller, you can simply reference the value, not the promise as I showned you before. So you don’t have to write code inside a then function, thesefore the code looks cleaner and easier to read.

<!doctype html>
<html ng-app="doublecloud" >
<head>
  <title>AngularJS Demo for Loaading Data via HTTP</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
  <script src="app.js"></script>
</head>
 
<body ng-view></body>
 
</html>
var app = angular.module('doublecloud', []);
 
app.config(function($routeProvider){
  $routeProvider
    .when('/',{
	  controller:'MainCtrl',
      template:'<div>data1:{{data1}}</div>',
      resolve:{
       '' : function(LoadService) { return LoadService.loadPromise; }
    }})
  })
 
app.controller('MainCtrl', function($scope, LoadService) {
  $scope.data1 = LoadService.data1;
});
 
app.service('LoadService', function($http) {
  var self = this;
 
  this.loadPromise = $http.get('data.json').success(function (data) {
      self.data1 = data;
  });
});
This is sample data.

Download Multiple URLs

More often than not, you will have multiple configuration resources to download. You can use multiple promises and add them into the resolve object of $routeProvider part. There are actually a better way to achieve that with much simpler and cleaner syntax.

The key is to use the $q service which can aggregate multiple promises. We can push down the orginal $http promise into the $q.all() as shown in the code. You don’t need to change other part of the code to make it work. Of course, you want to use additional values therefore you need to change the template and controller a bit.

With this generic solution, you can even use it when there is only one URL to download. Whenever you have something new to add, you can copy/paste the existing $http code and only change the name of the URL and what value to assign to.

 
var app = angular.module('doublecloud', []);
 
app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>data1:{{data1}}</div><div>data2:{{data2}}</div>',
    resolve:{
      '' : function(LoadService) {
        return LoadService.loadPromise;
      }
    }})
  })
 
app.controller('MainCtrl', function($scope, LoadService) {
  $scope.data1 = LoadService.data1;
  $scope.data2 = LoadService.data2;
});
 
app.service('LoadService', function($http, $q) {
  var self = this;
 
  this.loadPromise = $q.all([
 
    $http.get('data.json').success(function (data) {
      self.data1 = data;
    }),
 
    $http.get('data2').success(function (data) {
      self.data2 = data;
    })
 
  ]);
});

Discussion
Althougth HTTP is one of the top use cases of promise, but it’s not limited there. The above techniques can be applied on any tasks that return promises.

While it works, you probably don’t want to over-use it for fetching big amount of data due to the amount of time which can cause noticeable delay in GUI rendering. For small amount of configuration data, it works perfectly!

  1. No comments yet.
  1. No trackbacks yet.