Simple Server Polling in AngularJS Done Right

Plunker is here.

Syncing with the server these days is much easier with libraries like socket.io and alike. But, even though there is no need to mess with insane black magic like Comet or Flash-Remoting...
- Sometimes all you need is just a simple polling.

In this example we'll see how to implement recursive polling to poll the server for updates. The example is in AngularJS 1.5.x ES5 but the approach is valid for any framework and/or plain javascript.

The key to recursive-polling, and what make it different from just polling, is that we wait for the response before we initiate the next interval. This means that even if we poll the server very fast and the server is slow to response we avoid issues like race-condition and overloading the server.

Live demo

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

app.controller('MainCtrl', function($scope, $http, $timeout) {

  var loadTime = 1000, //Load the data every second
    errorCount = 0, //Counter for the server errors
    loadPromise; //Pointer to the promise created by the Angular $timout service

  var getData = function() {
    $http.get('http://httpbin.org/delay/1?now=' + Date.now())

    .then(function(res) {
      $scope.data = res.data.args;

      errorCount = 0;
      nextLoad();
    })

    .catch(function(res) {
      $scope.data = 'Server error';
      nextLoad(++errorCount * 2 * loadTime);
    });
  };

  var cancelNextLoad = function() {
    $timeout.cancel(loadPromise);
  };

  var nextLoad = function(mill) {
    mill = mill || loadTime;

    //Always make sure the last timeout is cleared before starting a new one
    cancelNextLoad();
    $timeout(getData, mill);
  };


  //Start polling the data from the server
  getData();


  //Always clear the timeout when the view is destroyed, otherwise it will keep polling and leak memory
  $scope.$on('$destroy', function() {
    cancelNextLoad();
  });

  $scope.data = 'Loading...';
});

In the example we load some dummy data from http://httpbin.org. We try to load it every 1000 milliseconds (1 second) but the server takes more than a second to respond. Using recursive polling saves us from some issues in this case.

When the server has an error (line 19) we generally wouldn't want to just stop the polling all together. It might be a just a temporary hiccup. We increase the timeout to the next load based on the count of response errors. nextLoad(++errorCount * 2 * loadTime);
So if the server issues are more serious we won't be hammering it. Eventually when it's fixed we'll go back to normal polling. errorCount = 0;

You can put this mechanism into a centralized service but always control it from the controllers. Start polling when the view is initialized and stop when the view is destroyed.

$scope.$on('$destroy', function() {
    cancelNextLoad();
  });

This way every view will be polling the server when it needs to and only then.

Guy A

Read more posts by this author.