19

データを非同期に取得するサービス ($http または $resource) を作成しています。最初は空の配列を返すことで、非同期であることを隠すことができますが、最終的にはデータが取り込まれます。

.factory('NewsfeedService1', ['$http', function($http) {
   var posts = [];
   var server_queried = false;
   return {
      posts: function() {
         if(!server_queried) {
            $http.get('json1.txt').success(
              function(data) {
                server_queried = true;
                angular.copy(data, posts);
            });
         }
         return posts;
      }
   };
}])
.controller('Ctrl1', ['$scope','NewsfeedService1',
function($scope, NewsfeedService1) {
    $scope.posts = NewsfeedService1.posts();
}])

または、promise を返すことで非同期性を公開できます。

.factory('NewsfeedService2', ['$http', function($http) {
  var posts = [];
  var server_queried = false;
  var promise;
  return {
     posts_async: function() {
       if(!promise || !server_queried) {
         promise = $http.get('json2.txt').then(
           function(response) {
              server_queried = true;
              posts = response.data;
              return posts;
         });
       }
       return promise;
     }
  };
}])

.controller('Ctrl2', ['$scope','NewsfeedService2',
function($scope, NewsfeedService2) {
  NewsfeedService2.posts_async().then(
    function(posts) {
      $scope.posts = posts;
  });
  // or take advantage of the fact that $q promises are
  // recognized by Angular's templating engine:
  // (note that Peter and Pawel's AngularJS book recommends against this, p. 100)
  $scope.posts2 = NewsfeedService2.posts_async();
}]);

( Plunker - 上記の 2 つの実装を試してみたい場合。)

非同期性を公開することの潜在的な利点の 1 つは、メソッドにエラー ハンドラーを追加することでコントローラーのエラーを処理できることthen()です。ただし、アプリケーション全体のインターセプターで $http エラーをキャッチして処理する可能性があります。

では、いつサービスの非同期性を公開する必要があるのでしょうか?

4

3 に答える 3

9

このフェンスの両側に人がいると思います。個人的には、ライブラリまたは関数の非同期性を常に公開する必要があると感じています (より正確には、ライブラリまたは関数の非同期性を決して隠してはならないと感じています)。主な理由は透明性です。たとえば、これは機能しますか?

app.controller('MyController', function(NewsfeedService) {
  $scope.posts = NewsfeedService.posts();
  doSomethingWithPosts($scope.posts); // <-- will this work?
});

最初の方法 (例: ) を使用している場合、技術的には配列ですが$resource、そうではありません。独自の非同期操作がある場合、競合状態になる可能性があります$scope.postsdoSomethingWithPosts代わりに、とにかく非同期コードを使用する必要があります。

app.controller('MyController', function(NewsfeedService) {
  $scope.posts = NewsfeedService.posts(function() {
    doSomethingWithPosts($scope.posts);
  });
});

(もちろん、コールバックpostsが を引数として受け入れるようにすることもできますが、それでもわかりにくく、標準的ではないと思います。)

幸いなことに、私たちにはプロミスがあり、プロミスのまさにその目的は、操作の将来の価値を表すことです。さらに、Angular の$qライブラリで作成された promise はビューにバインドできるため、これに問題はありません。

app.controller('MyController', function(NewsfeedService) {
  $scope.posts = NewsfeedService.posts();
  // $scope.posts is a promise, but when it resolves
  // the AngularJS view will work as intended.
});

[更新: promise をビューに直接バインドすることはできなくなりました。promise が解決されるのを待って、スコープ プロパティを手動で割り当てる必要があります。]

余談ですが、 の人気のある代替手段であるRestangular$resourceは promiseを使用しており、AngularJS 自身$resourceは 1.2 でそれらをサポートする予定です (最新の 1.1.x では既にサポートされている可能性があります)。

于 2013-07-02T03:56:02.120 に答える
5

基礎となるフレームワークの非同期の性質を隠すのは好きではないので、私は常に非同期オプションを使用します。

同期バージョンは、それを使用している間はよりきれいに見えるかもしれませんが、開発者が呼び出しが本質的に非同期であることを認識せず、呼び出しを行った後にデータにアクセスしようとするバグにうっかりつながります。

$resourceSO は、本質的に同期していると見なし、応答を期待して人々がこの間違いを犯すという質問でいっぱいです。$resourceまた、オプション 1 と同様のアプローチをとります。ここでは、呼び出しが完了した後に結果が入力されますが、それでも$resource成功と失敗の関数が公開されます。

Promise が返された場合、AngularJS は非同期呼び出しの複雑さを隠そうとします。そのため、Promise に直接バインドすると、同期呼び出しを行っているように感じられます。

于 2013-07-02T03:59:06.020 に答える
5

この方法で構築された複数のサービスを操作するのが難しくなるため、私はノーと言います。promiseを使用$q.all()すると、複数のリクエストを作成し、すべてが完了したときに応答することができます。また、promise を渡すことで操作を連鎖させることもできます。

同期スタイルのサービスでこれを行う直感的な方法はありません。

于 2013-07-02T04:07:19.923 に答える