AngularJSを使用したサーバーポーリング


86

私は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秒間スリープして低速サーバーをシミュレートすると、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);
    })();
};

これは、サーバーの応答が速い場合にのみ機能します。遅延がある場合は、応答を待たずに1秒間に1リクエストをスパム送信し、UIをクリアしているようです。コールバック関数を使う必要があると思います。私は試した:

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

しかし、エラーが発生しました:「エラー:destination.pushは関数ではありません」これは$ resourceのドキュメントに基づいていましたが、そこでの例を本当に理解していませんでした。

2番目のアプローチを機能させるにはどうすればよいですか?

回答:


115

tickのコールバックで関数を呼び出す必要がありますquery

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

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

3
素晴らしい、ありがとう。そこにコールバックを置くことができるとは知りませんでした。これでスパムの問題は解決しました。また、UIクリアの問題を解決するために、データ割り当てをコールバック内に移動しました。
デイブ

1
お役に立ててうれしいです!これで問題が解決した場合は、この回答を受け入れて、後で他の人もその恩恵を受けることができます。
abhaga 2012

1
上記のコードがpageAとcontrollerA用であると仮定します。pageBとcontrollerBに移動するときにこのタイマーを停止するにはどうすればよいですか?
Varun Verma 2013年

6
$ timeoutを停止するプロセスについては、docs.angularjs.org / api/ng。$timeoutで説明されています。基本的に、$ timeout関数は、変数に割り当てる必要のあるpromiseを返します。次に、そのコントローラーがいつ破壊されるかをリッスンします:$ scope。$ on( 'destroy'、fn());。コールバック関数で$ timeoutのcancelメソッドを呼び出し、保存したpromiseを渡します。$ timeout.cancel(timeoutVar); $間隔のドキュメントは、実際にはより良い例(持ってdocs.angularjs.org/api/ng.$interval
ジャスティン・ルーカス

1
@JustinLucas、万が一$ scope。$ on( '$ destroy'、fn());
トマト2014

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;
    }
});

17
-1、次のリクエストを送信する前にサーバーの応答を待つことができないため、$ intervalは適切ではないと思います。これにより、サーバーの待ち時間が長い場合に多くのリクエストが発生する可能性があります。
Treur 2014年

4
@Treur:それは最近の常識のようですが、同意するかどうかはわかりません。ほとんどの場合、より回復力のあるソリューションが必要です。ユーザーが一時的にオフラインになる場合、またはサーバーが単一の要求に応答しない極端な場合を考えてみます。新しいタイムアウトが設定されないため、$ timeoutのユーザーのUIの更新は停止します。$ intervalのユーザーの場合、接続が復元されるとすぐに、UIは中断したところから再開します。当然のことながら、適切な遅延を選択することも重要です。
ボブ

2
便利ですが、弾力性はないと思います。(私の寝室のトイレも夜はとても便利ですが、最終的には悪臭がし始めます;))$ intervalを使用して実際のデータを取得するときは、サーバーの結果を無視します。これには、ユーザーに通知したり、データの整合性を促進したり、つまり、アプリケーションの状態を一般的に管理したりする方法がありません。ただし、これには一般的な$ httpインターセプターを使用して、これが発生したときに$ intervalをキャンセルすることができます。
Treur 2014年

2
$ q promiseを使用している場合は、finallyコールバックを使用するだけで、要求が失敗したかどうかに関係なくポーリングが続行されることを確認できます。
タイソンネロ

8
より良い代替策は、成功イベントだけでなくエラーイベントも処理することです。このようにして、失敗した場合にリクエストを再試行できます。あなたも...異なる間隔でそれを行う可能性があります
ピーナッツ

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...';
   });

0

$ intervalサービスを使用して簡単にポーリングを行うことができます。これは$ intervalに関する詳細なドキュメントです
https://docs.angularjs.org/api/ng/service/$interval$intervalの 使用に関する
問題は、$ httpサービスの呼び出しまたはサーバーの相互作用を行っている場合、および$ interval時間より長く遅延した場合です。次に、1つのリクエストが完了する前に、別のリクエストを開始します。
解決策:
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;
    }
}]);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.