Polling a server using AngularJS

I am trying to learn AngularJS. My first attempt to get new data every second:

'use strict'; function dataCtrl($scope, $http, $timeout) { $scope.data = []; (function tick() { $http.get('api/changingData').success(function (data) { $scope.data = data; $timeout(tick, 1000); }); })(); }; 

When I simulate a slow server, a sleep stream for 5 seconds, it waits for a response before updating the user interface and setting a different timeout. The problem is what I rewrote above to use Angular modules and DI to create a module:

 'use strict'; angular.module('datacat', ['dataServices']); angular.module('dataServices', ['ngResource']). factory('Data', function ($resource) { return $resource('api/changingData', {}, { query: { method: 'GET', params: {}, isArray: true } }); }); function dataCtrl($scope, $timeout, Data) { $scope.data = []; (function tick() { $scope.data = Data.query(); $timeout(tick, 1000); })(); }; 

This only works if the server response is fast. If there is any delay, it sends out 1 request per second without waiting for a response and seems to clear the user interface. I think I need to use the callback function. I tried:

 var x = Data.get({}, function () { }); 

but an error was received: "Error: destination.push is not a function." This was based on the docs for $ resource , but I really didn't understand the examples there.

How to make the second approach work?

+83
javascript angularjs
Dec 02
source share
4 answers

You must call the tick function in the callback for query .

 function dataCtrl($scope, $timeout, Data) { $scope.data = []; (function tick() { $scope.data = Data.query(function(){ $timeout(tick, 1000); }); })(); }; 
+112
Dec 02
source share

Later versions of angular introduced $ interval , which works even better than $ timeout for server polling.

 var refreshData = function() { // Assign to scope within callback to avoid data flickering on screen Data.query({ someField: $scope.fieldValue }, function(dataElements){ $scope.data = dataElements; }); }; var promise = $interval(refreshData, 1000); // Cancel interval on page changes $scope.$on('$destroy', function(){ if (angular.isDefined(promise)) { $interval.cancel(promise); promise = undefined; } }); 
+32
Jan 29 '14 at 5:23
source share

Here is my version using recursive polling. This means that it will wait for a server response before the start of the next timeout. In addition, if an error occurs, he will continue the survey, but in a more relaxed estate and depending on the duration of the error.

Demo here

It is written in more detail about it here

 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 $scope.$on('$destroy', function() { cancelNextLoad(); }); $scope.data = 'Loading...'; }); 
+1
Aug 10 '16 at 2:02
source share

We can easily do polls using the $ interval service. here is a detailed document on $ interval
https://docs.angularjs.org/api/ng/service/$interval
The problem with using $ interval is that if you make an HTTP service call or interact with the server, and if the delay exceeds the time interval, then before your one request is completed, it starts another request.
<b> Solution:
1. The survey should be a simple receipt of status from the server, like a single bit or light json, so it should not take more time than your specific time interval. You must also determine the interval time to avoid this problem.
2. Somehow this all happens for some reason, you should check the global flag that the previous request was completed or not, before sending any other requests. He will skip this time interval, but he will not send the request prematurely.
Also, if you want to set a threshold value that should be set after a certain value, then you can do it as follows.
Here is a working example. explained in detail here

 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; } }]); 
0
Apr 02 '17 at 5:00
source share



All Articles