Javascript 使用 AngularJS 进行服务器轮询

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13671031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 12:37:48  来源:igfitidea点击:

Server polling with AngularJS

javascriptangularjs

提问by Dave

I'm trying to learn AngularJS. My first attempt to get new data every second worked:

我正在尝试学习 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);
        });
    })();
};

When I simulate a slow server by sleeping the thread for 5 seconds it waits for the response before updating the UI and setting another timeout. The problem is when I rewrote the above to use Angular modules and DI for module creation:

当我通过让线程休眠 5 秒来模拟慢速服务器时,它会在更新 UI 并设置另一个超时之前等待响应。问题是当我重写上述内容以使用 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);
    })();
};

This only works if the server response is fast. If there's any delay it spams out 1 request a second without waiting for a response and seems to clear the UI. I think I need to use a callback function. I tried:

这仅在服务器响应快速时才有效。如果有任何延迟,它会在不等待响应的情况下每秒发送 1 个请求,并且似乎清除了 UI。我想我需要使用回调函数。我试过:

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

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

但出现错误:“错误:destination.push 不是函数”这是基于$resource的文档,但我并没有真正理解那里的示例。

How do I make the second approach work?

我如何使第二种方法起作用?

回答by abhaga

You should be calling the tickfunction in the callback for query.

您应该tick在 的回调中调用该函数query

function dataCtrl($scope, $timeout, Data) {
    $scope.data = [];

    (function tick() {
        $scope.data = Data.query(function(){
            $timeout(tick, 1000);
        });
    })();
};

回答by Bob

More recent versions of angular have introduced $intervalwhich works even better than $timeoutfor server polling.

较新版本的 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;
    }
});

回答by guya

Here is my version using recursive polling. Which means it'll wait for the server response before initiating the next timeout. Also, when an error occur it'll continue polling but in a more relaxed manor and according to the duration of the error.

这是我使用递归轮询的版本。这意味着它将在启动下一次超时之前等待服务器响应。此外,当发生错误时,它将继续轮询,但会根据错误的持续时间以更轻松的方式进行。

Demo is here

演示在这里

Written more about it in 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...';
   });

回答by user1941574

We can do it polling easily using $interval service. here is detail document about $interval
https://docs.angularjs.org/api/ng/service/$interval
Problemusing $interval is that if you are doing $http service calling or server interaction and if delayed more than $interval time then before your one request completes, it starts another request.
Solution:
1. Polling should be simple status getting from server like a single bit or lightweight json so should not take longer then your defined interval time. You should also define time of interval appropriately to avoid this issue.
2. Somehow it is still happening due any reason, you should check a global flag that previous request finished or not before sending any other requests. It will miss that time interval but it won't send request prematurely.
Also if you wanted to set threshold value that after some value anyhow polling should be set then you can do it following way.
Here is working example. explained in detail here

我们可以使用 $interval 服务轻松进行轮询。这是有关 $interval
https://docs.angularjs.org/api/ng/service/$interval 的详细文档, 使用 $interval 的
问题是,如果您正在进行 $http 服务调用或服务器交互,并且延迟超过 $interval 时间然后在您的一个请求完成之前,它会启动另一个请求。
解决方案:
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;
    }
}]);