Saturday, April 1, 2017

Polling from angularJs ($interval)

Recently come across the situation that we need to get status for one of disconnected service and show on browser.  We end up doing polling from angular Js $interval service. We could have use SignalR features of asp.net for same thing  but wanted to do it more simple way.

We can do it polling easily using $interval service.
here is detail document about $interval  https://docs.angularjs.org/api/ng/service/$interval

Problem using $interval is that if you are doing $http service calling or server interaction and if server delayed response more than $interval time then before your one request completes, it starts another request. your next interval will be in race with previous response.

Solution:

1. To avoid  above problem, polling should be simple status getting from server like a single bit or very lightweight json so it should be complete within your defined interval time. You should also define time of interval appropriately to avoid this issue.

2. If somehow it is still happening due to any reason, you should check a global flag that checks previous request finished or not before sending any other requests. It will miss that time interval but it won't send request prematurely.

Meaning if you set $interval time as 1000 milliseconds and it sent first  get request to server and that request didn't sent response yet before 1000 milliseconds and your second interval starts before completes previous one.

Someone has resolve this issue using just $timeout service. here is the blog.  This guy has used recursion to resolve this issue. meaning you can call same polling function from resulted promise. This is good but i believe that recursion saves function call stack.  All browser has its one limit of stack.  browser stack limit also depend on other factors  like memory, open number of tabs etc.

Someone has done very nice research on it.

https://www.nczonline.net/blog/2009/05/19/javascript-stack-overflow-error/
https://web.archive.org/web/20110128022845/http://www.javascriptrules.com/2009/06/30/limitation-on-call-stacks/

You can also perform some test using following url
http://adamrich.name/recursion.html

Another problem with using $interval service is, We should stop interval after some threshold value because it may happens that server has some issue or error and it may never turn in to your desired result from server to stop your interval.

Here is working example where you can use $interval solve both of above problems.

'use strict';

angular.module('myApp.view2', ['ngRoute'])
.controller('View2Ctrl', ['$scope', '$timeout', '$interval', '$http', function ($scope,  $timeout, $interval, $http) {
        $scope.title = "Test Title";

        $scope.data = [];

        var hasvaluereturnd = true; // Flag to check
        var thresholdvalue = 20; // interval threshold value

        function poll(interval, callback) {
            return $interval(function () {
                if (hasvaluereturnd) {  //check flag before start new call
                    callback(hasvaluereturnd);
                }
                thresholdvalue = thresholdvalue - 1;  //Decrease threshold value
                if (thresholdvalue == 0) {
                    $scope.stopPoll(); // Stop $interval if it reaches to threshold
                }
            }, interval)
        }

        var pollpromise = poll(1000, function () {
            hasvaluereturnd = false;
            //$timeout(function () {  // You can test scenario where server 
                                      takes more time then interval
            $http.get('http://httpbin.org/get?timeoutKey=timeoutValue').then(
                function (data) {
                    hasvaluereturnd = true// set Flag to true to start new call
                    $scope.data = data;

                },
                function (e) {
                    hasvaluereturnd = true; // set Flag to true to start new call
                    //You can set false also as per your requirement in case of error
                }
            );
            //}, 2000);
        });

        // stop interval.
        $scope.stopPoll = function () {
            $interval.cancel(pollpromise);
            thresholdvalue = 0;     //reset all flags.
            hasvaluereturnd = true;
        }

    }]);

Reference:

Above code just wrote on angular js seed boilerplate  code for angularjs 1.x. Github location is here
Also, we have used http://httpbin.org/  for any third party test call. Which is very nice  to test all http library. you can send any type of request and get response.

6 comments: