使用AngularJS进行服务器轮询

Dav*_*ave 85 javascript angularjs

我正在努力学习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);
        });
    })();
};
Run Code Online (Sandbox Code Playgroud)

当我通过将线程休眠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);
    })();
};
Run Code Online (Sandbox Code Playgroud)

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

var x = Data.get({}, function () { });
Run Code Online (Sandbox Code Playgroud)

但得到一个错误:"错误:destination.push不是一个函数"这是基于$ resource的文档,但我真的不明白那里的例子.

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

abh*_*aga 115

你应该tick在回调中调用函数query.

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

    (function tick() {
        $scope.data = Data.query(function(){
            $timeout(tick, 1000);
        });
    })();
};
Run Code Online (Sandbox Code Playgroud)

  • 停止$ timeout的过程在http://docs.angularjs.org/api/ng.$timeout中解释.基本上,$ timeout函数返回一个需要分配给变量的promise.然后监听该控制器何时被销毁:$ scope.$ on('destroy',fn());. 在回调函数中调用$ timeout的cancel方法并传入你保存的promise:$ timeout.cancel(timeoutVar); $ interval文档实际上有一个更好的例子(http://docs.angularjs.org/api/ng.$interval) (6认同)
  • 非常好,谢谢.我不知道你可以把回调放在那里.这解决了垃圾邮件问题.我还将数据分配移动到回调内部,解决了UI清除问题. (3认同)

Bob*_*Bob 33

更新版本的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;
    }
});
Run Code Online (Sandbox Code Playgroud)

  • -1,我不认为$ interval是合适的,因为在发送下一个请求之前你不能等待服务器响应.当服务器具有高延迟时,这可能导致许多请求. (15认同)
  • 更好的选择是不仅要处理成功事件,还要处理错误事件.这样,如果请求失败,您可以再次尝试请求.你甚至可以在不同的时间间隔做... (8认同)
  • @Treur:虽然这些日子似乎是传统智慧,但我不确定我是否同意.在大多数情况下,我宁愿拥有更具弹性的解决方案.考虑用户临时脱机或者服务器不响应单个请求的极端情况.由于不会设置新超时,UI将停止为$ timeout用户更新.对于$ interval的用户,只要连接恢复,UI就会从中断处继续.显然,选择合理的延迟也很重要. (4认同)
  • 我认为它更方便,但不具备弹性.(我卧室的厕所在晚上也很方便,但最终会开始闻起来很糟糕;))使用$ interval检索实际数据时,忽略服务器结果.这缺乏一种方法来通知您的用户,促进数据完整性或简而言之:通常管理您的应用程序状态.但是,您可以使用常见的$ http拦截器,并在发生这种情况时取消$ interval. (2认同)
  • 如果使用$ q promises,您可以简单地使用finally回调来确保轮询是否继续,无论请求是否失败. (2认同)

guy*_*uya 5

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

演示在这里

在这里写了更多关于它的信息

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...';
   });
Run Code Online (Sandbox Code Playgroud)