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

AngularJS $http-вызов в службе, возврат разрешенных данных, а не promises

Я хочу знать, можно ли сделать вызов службы, который использует $http, чтобы он возвращал данные напрямую, не возвращая обещание? Я попытался использовать $q и отложить его без везения.

Вот что я имею в виду:

У меня есть служба:

angular.module('myModule').factor('myService', ['$http', '$q',
    function($http, $q) {

        // Public API
        return {
            myServiceCall: function() {
                return $http.get('/server/call');
            }
        };
    }
]);

И так я бы назвал это:

// My controller:
myService.myServiceCall().then(function(data) {
  $scope.data = data;
});

Я хочу этого избежать и вместо этого хочу:

$scope.data = myService.myServiceCall();

Я хочу, чтобы он был полностью разрешен в этой строке, возможно ли это?

Я попробовал некоторые комбинации методов $q, defer и 'then', но не может показаться, что это правильно, поскольку метод немедленно возвращается.

Edit:

Если вам интересно, почему, основная причина в том, что я хотел упростить код контроллера, это легко сделать с помощью ngResource, поскольку эти вызовы автоматически разрешаются в шаблонах, поэтому я хотел избежать необходимости делать все ". then 'everytime.

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

Я думаю, что пока некоторые из вас указали, я буду использовать ресурс $для решения довольно близко.

4b9b3361

Ответ 1

Я могу придумать два способа сделать это. Первое, безусловно, лучше второго.

Первый способ: использование свойства resolve маршрутов

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

Например, скажем, у вас есть следующая услуга:

app.service('things', ['$http', '$q', function ($http, $q) {
    var deferred = $q.defer();

    $http({
        method: 'GET',
        url: '/things',
        cache: true
    }).success(function (data) {
        deferred.resolve(data);
    }).error(function (msg) {
        deferred.reject(msg);
    });

    return deferred.promise;
}]);

Затем вы можете определить свой маршрут примерно так:

$routeProvider.when('/things', {
    controller: ['$scope', 'thingsData', function ($scope, thingsData) {
        $scope.allThings = thingsData;
    }],
    resolve: {
        thingsData: ['things', function (things) {
            return things;
        }]
    }
});

Ключ в объекте resolve соответствует имени второй зависимости для контроллера, поэтому, хотя он возвращает обещание, это обещание будет разрешено до запуска контроллера.

Второй способ: Сделайте запрос задолго до того, как вам понадобится данные

Второй способ, который совсем не идеален и, вероятно, в какой-то момент укусит вас на заднице, заключается в том, чтобы сделать запрос на данные на раннем этапе, например, когда ваше приложение инициализировано, а затем на самом деле получить позже, скажем, на странице, для которой требуется 3 щелчка, чтобы перейти с домашней страницы. Пальцы пересекаются, если звезды выровнены, ваш запрос HTTP будет возвращен к тому времени, и вы будете иметь данные для использования.

angular.module('myModule').factory('myService', ['$http', '$q', function($http, $q) {
    var data = [];

    function fetch() {
        $http.get('/server/call').then(function (d) { data = d; });
    }

    // Public API
    return {
        fetchData: fetch,
        myServiceCall: function () {
            return data;
        }
    };
}]);

Затем в функции запуска модуля выполните запрос:

angular.module('myModule').run(['myService', function (myService) {
    myService.fetchData();
});

Затем в контроллере для страницы, которая будет работать как минимум через 3 секунды после запуска приложения, вы можете сделать так, как вы предположили:

 angular.module('myModule').controller('deepPageCtrl', ['$scope', 'myService', function ($scope, myService) {
      $scope.data = myService.myServiceCall();
      if (angular.isArray($scope.data) && $scope.data.length === 0) {
           panic("data isn't loaded yet");
      }
  }]);

Бонус, одинаково плохой третий способ: Синхронный запрос AJAX

Сделайте так, как предлагает ricick, и используйте синхронный запрос AJAX с помощью метода jQuery .ajax().

Ответ 2

Это невозможно, поскольку HTTP-вызовы по сути являются асинхронными. То есть, вам нужно дождаться ответа сервера.

Служба службы $resource обойдется в этом, возвращая объект-прокси, который заполняется возвращаемыми данными на сервере, но он все еще не полностью разрешен в одной строке.

Edit:

На самом деле можно сделать синхронные HTTP-запросы с помощью jQuery ajax-метода, установив опцию "async" равным false.

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

Подробнее см. в документах:

http://api.jquery.com/jQuery.ajax/

Ответ 3

Давным-давно (в мире angular, фактически в прошлом году), на самом деле это было так.

Но теперь это уже не так, я думаю, это из-за различий в поведении в плане перспективы и контроля, здесь больше об устаревании: Предложение Angularjs не привязывается к шаблону в 1.2