2012-12-02 15 views
83

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

を、私はそれがUIを更新し、別のタイムアウトを設定する前に応答を待つ5秒間スレッドを眠っていることにより、低速なサーバーをシミュレートする場合。

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

のみ機能サーバーの応答が速い場合:私は、モジュールを作成するための角度モジュールやDIを使用するには、上記を書き換えたときに問題があります。遅延があると、応答を待つことなく1秒間に1リクエストをスパムし、UIをクリアするようです。私はコールバック関数を使用する必要があると思う。私が試した:

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

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

2番目のアプローチはどのように動作させるのですか?

答えて

112

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

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

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

優れた、ありがとう。私はあなたがそこにコールバックを置くことができるのか分からなかった。それがスパム問題を解決しました。私はまた、UIクリアリング問題を解決したコールバックの内部にデータ割り当てを移動しました。 – David

+1

助けてくれるのはうれしい!これで問題が解決した場合は、この回答を受け入れることができます。 – abhaga

+1

上記のコードはpageAとcontrollerAのコードであると仮定します。私はpageBとcontrollerBに移動するときにこのタイマーを止めるにはどうすればいいですか? –

32

最近のバージョンでは、サーバーのポーリングのため$timeoutよりもさらに良い作品$intervalを導入しています。

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

-1、次のリクエストを送信する前にサーバーの応答を待つことができないため、$ intervalが適切ではないと思います。これにより、サーバーのレイテンシが高い場合に多くの要求が発生する可能性があります。 – Treur

+4

@Treur:これは最近の常識であるように思えますが、私は同意していません。ほとんどの場合、私はむしろより弾力のある解決策を持っています。ユーザが一時的にオフラインになった場合や、サーバが1回のリクエストに応答しない場合の極端な状況を考えてみましょう。新しいタイムアウトが設定されないため、UIは$ timeoutのユーザーの更新を停止します。 $ intervalのユーザーの場合、接続が復元されるとすぐにUIがピックアップされます。明らかに遅れをとらえることも重要です。 – Bob

+2

私はそれがより便利だと思いますが、弾力はありません。 (私の寝室のトイレも夜は非常に便利ですが、最終的に悪臭を覚えるようになります;))$ intervalを使って実際のデータを検索すると、サーバーの結果は無視されます。これには、ユーザーに知らせる方法、データの整合性を簡単にする方法、またはアプリケーションの状態を一般的に管理する方法がありません。 しかし、これには一般的な$ httpインターセプタを使用して、$ intervalをキャンセルすることができます。 – Treur

1

ここは再帰的ポーリングを使用した私のバージョンです。これは、次のタイムアウトを開始する前に、サーバーの応答を待つことを意味します。 また、エラーが発生した場合、ポーリングは継続されますが、エラーの持続時間に応じて、よりリラックスした時間になります。

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

我々は$間隔のサービスを使用して簡単にポーリングを行うことができます。ここ は$間隔約$間隔を使用して
https://docs.angularjs.org/api/ng/service/$interval
問題あなたは$ HTTPサービスの呼び出しやサーバとの対話を行うと、あなたの1つの要求は、それを完了する前に、次に$間隔時間よりも遅れている場合している場合ということです詳細ドキュメントです別のリクエストを開始します。
ソリューション:
1.ポーリングは、単純なステータスは単一ビットまたは軽量なJSONのようなサーバから取得する必要がありますので、その後、長くお使い定義された間隔の時間を取るべきではありません。この問題を回避するには、間隔の時間も適切に定義する必要があります。
2.何らかの理由で何とか起きている場合、前のリクエストが完了したかどうかを確認する必要があります。その時間間隔が間に合わなくなりますが、リクエストを早めに送信することはありません。
また、何らかの値の後にポーリングを設定する必要がある場合には、次のようにしきい値を設定することもできます。
ここに実例があります。詳細に説明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; 
    } 
}]); 
関連する問題