16

次のように、コントローラー内の非同期サービスからデータを取得しています。

myApp.controller('myController', ['$scope', 'AsyncService',
function($scope, AsyncService) {
    $scope.getData = function(query) {
        return AsyncService.query(query).then(function(response) {
            // Got success response, return promise
            return response;
        }, function(reason) {
            // Got error, query again in one second
            // ???
        });
    }
}]);

私の質問:

  1. 約束を返さずにサービスからエラーが発生したときに、サービスを再度クエリする方法。
  2. 私のサービスでこれを行う方が良いでしょうか?

ありがとう!

4

5 に答える 5

22

コントローラーではなく、サービス自体で要求を再試行できます。

したがって、AsyncService.query次のようになります。

AsyncService.query = function() {
  var counter = 0
  var queryResults = $q.defer()

  function doQuery() {
    $http({method: 'GET', url: 'https://example.com'})
      .success(function(body) {
        queryResults.resolve(body)
      })
      .error(function() {
        if (counter < 3) {
          doQuery()
          counter++ 
        }
      })
  }

  return queryResults.promise
}

そして、コントローラーでエラー関数を取り除くことができます:

myApp.controller('myController', ['$scope', 'AsyncService',
  function($scope, AsyncService) {
    $scope.getData = function(query) {
      return AsyncService.query(query).then(function(response) {
        // Got success response
        return response;
      });
    }
  }
]);
于 2013-10-25T17:42:06.637 に答える
2

再帰を使用しない指数バックオフの実装を作成しました (ネストされたスタック フレームが作成されますよね?) 実装方法には、複数のタイマーを使用するコストがかかり、常に make_single_xhr_call のすべてのスタック フレームを作成します (成功した後でも) 、障害後のみではなく)。それだけの価値があるかどうかはわかりませんが (特に平均的なケースが成功した場合)、検討の材料にはなります。

呼び出し間の競合状態が心配でしたが、javascript がシングルスレッドでコンテキスト スイッチがない場合 (これにより、1 つの $http.success が別の $http.success に割り込まれ、2 回実行される可能性があります)、ここで問題ありません。正しい?

また、私は angularjs と最新の javascript に非常に慣れていないため、規則も少し汚れている可能性があります。どう考えているか教えてください。

var app = angular.module("angular", []);

app.controller("Controller", ["$scope", "$http", "$timeout",
    function($scope, $http, $timeout) {

  /**
   * Tries to make XmlHttpRequest call a few times with exponential backoff.
   * 
   * The way this works is by setting a timeout for all the possible calls
   * to make_single_xhr_call instantly (because $http is asynchronous) and
   * make_single_xhr_call checks the global state ($scope.xhr_completed) to
   * make sure another request was not already successful.
   *
   * With sleeptime = 0, inc = 1000, the calls will be performed around:
   * t = 0
   * t = 1000 (+1 second)
   * t = 3000 (+2 seconds)
   * t = 7000 (+4 seconds)
   * t = 15000 (+8 seconds)
   */
  $scope.repeatedly_xhr_call_until_success = function() {
    var url = "/url/to/data";
    $scope.xhr_completed = false
    var sleeptime = 0;
    var inc = 1000;
    for (var i = 0, n = 5 ; i < n ; ++i) {
      $timeout(function() {$scope.make_single_xhr_call(url);}, sleeptime);
      sleeptime += inc;
      inc = (inc << 1); // multiply inc by 2
    }
  };

  /**
   * Try to make a single XmlHttpRequest and do something with the data.
   */
  $scope.make_single_xhr_call = function(url) {
    console.log("Making XHR Request to " + url);

    // avoid making the call if it has already been successful
    if ($scope.xhr_completed) return;
    $http.get(url)
      .success(function(data, status, headers) {
        // this would be later (after the server responded)-- maybe another
        // one of the calls has already completed.
        if ($scope.xhr_completed) return;
        $scope.xhr_completed = true;
        console.log("XHR was successful");
        // do something with XHR data
      })
      .error(function(data, status, headers) {
        console.log("XHR failed.");
      });
  };

}]);
于 2014-11-18T21:35:21.770 に答える