Подтвердить что ты не робот

Должны ли услуги выявлять их асинхронность?

Я пишу службу, которая будет извлекать данные асинхронно ($ 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();
}])

Или я могу разоблачить асинхронность, возвращая обещание:

.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 templating engine:
  // (note that Peter and Pawel AngularJS book recommends against this, p. 100)
  $scope.posts2 = NewsfeedService2.posts_async();
}]);

(Plunker - если кто-то хочет играть с этими двумя реализациями.)

Одним из потенциальных преимуществ разоблачения асихотроны было бы то, что я могу справиться с ошибками в контроллере, добавив обработчик ошибок в метод then(). Тем не менее, я, скорее всего, поймаю и столкнусь с ошибками $http в общесистемном interceptor.

Итак, когда должна раскрываться асинхронность службы?

4b9b3361

Ответ 1

Я предполагаю, что вы найдете людей по обеим сторонам этого забора. Лично я чувствую, что вы всегда должны раскрывать асинхронность библиотеки или функции (или более правильно: я считаю, что вы никогда не должны скрывать асинхронность библиотеки или функции). Основная причина - прозрачность; например, будет ли это работать?

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

Если вы используете первый метод (например, $resource), он не будет, хотя $scope.posts является технически массивом. Если doSomethingWithPosts имеет свои асинхронные операции, вы можете столкнуться с состоянием гонки. Вместо этого вы все равно должны использовать асинхронный код:

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

(Конечно, вы можете заставить callback принять posts как аргумент, но я все еще считаю его запутанным и нестандартным.)

К счастью, мы имеем promises, и сама цель обещания - представлять будущую ценность операции. Кроме того, поскольку promises, созданный с Angular $q, библиотеки могут привязываться к представлениям, в этом нет ничего плохого:

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

[Обновить: вы больше не можете привязывать promises к представлению; вы должны дождаться, когда обещание будет разрешено, и назначьте свойство области видимости вручную.]

В качестве альтернативы Restangular, популярной альтернативой $resource, используется promises, а собственный AngularJS $resource будут поддерживать их в 1.2 (они могут уже поддерживать их в последних версиях 1.1.x).

Ответ 2

Я всегда буду использовать опцию async, так как мне не нравится скрывать асинхронный характер базовой структуры.

Версия синхронизации может выглядеть более чистой при ее потреблении, но она непреднамеренно приводит к ошибке, когда разработчик не понимает, что вызов имеет асинхронный характер и пытается получить доступ к данным после совершения вызова.

SO заполняется вопросами, в которых люди делают эту ошибку с $resource, учитывая ее синхронизацию по своей природе и ожидая ответа. $resource также использует аналогичный подход к опции 1, где результаты заполняются после завершения вызова, но все же $resource предоставляет функцию успеха и отказа.

AngularJS пытается скрыть сложности асинхронных вызовов, если возвращается promises, поэтому привязка непосредственно к обещанию кажется, что вы выполняете вызов синхронизации.

Ответ 3

Я говорю "нет", потому что это затрудняет работу с несколькими службами, построенными таким образом. С помощью promises вы можете использовать $q.all() для выполнения множественного запроса и ответа, когда все они завершатся, или вы можете объединить операции вместе, передав обещание.

Не было бы интуитивного способа сделать это для службы синхронного стиля.