Я пытаюсь изучить AngularJS. Моя первая попытка получать новые данные каждую секунду сработала:
'use strict';
function dataCtrl($scope, $http, $timeout) {
$scope.data = [];
(function tick() {
$http.get('api/changingData').success(function (data) {
$scope.data = data;
$timeout(tick, 1000);
});
})();
};
Когда я имитирую медленный сервер, засыпая поток на 5 секунд, он ждет ответа перед обновлением пользовательского интерфейса и установкой другого тайм-аута. Проблема в том, что я переписал приведенное выше, чтобы использовать модули Angular и DI для создания модулей:
'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);
})();
};
Это работает, только если сервер отвечает быстро. Если есть какая-либо задержка, он отправляет 1 запрос в секунду, не дожидаясь ответа, и, кажется, очищает пользовательский интерфейс. Я думаю, мне нужно использовать функцию обратного вызова. Я старался:
var x = Data.get({}, function () { });
но получил сообщение об ошибке: «Ошибка: destination.push не является функцией» Это было основано на документации для $ resource, но я не совсем понял приведенные там примеры.
Как заставить второй подход работать?
источник
Более поздние версии angular представили $ interval, который работает даже лучше, чем $ timeout для опроса сервера.
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; } });
источник
Вот моя версия с рекурсивным опросом. Это означает, что он будет ждать ответа сервера, прежде чем инициировать следующий тайм-аут. Кроме того, при возникновении ошибки опрос будет продолжен, но в более спокойной обстановке и в зависимости от продолжительности ошибки.
Демо здесь
Подробнее об этом написано здесь
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...'; });
источник
Мы можем легко выполнить опрос с помощью сервиса $ interval. вот подробный документ о $ interval
https://docs.angularjs.org/api/ng/service/$interval
Проблема с использованием $ interval заключается в том, что если вы выполняете вызов службы $ http или взаимодействие с сервером и если задержка превышает $ interval time затем, прежде чем ваш один запрос завершится, он запускает другой запрос.
Решение:
1. Опрос должен представлять собой простой способ получения статуса от сервера, например, однобитовый или легкий json-файл, поэтому не должен занимать больше времени, чем установленный вами интервал времени. Вы также должны соответствующим образом определить время интервала, чтобы избежать этой проблемы.
2. Каким-то образом это все еще происходит по какой-либо причине, вы должны проверить глобальный флаг, завершен ли предыдущий запрос или нет, перед отправкой любых других запросов. Он пропустит этот временной интервал, но не отправит запрос преждевременно.
Также, если вы хотите установить пороговое значение, которое должно быть установлено после некоторого значения в любом случае, вы можете сделать это следующим образом.
Вот рабочий пример. подробно объяснено здесь
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; } }]);
источник