Как дождаться ответа от запроса $ http в angularjs?

93

Я использую данные из службы RESTful на нескольких страницах. Поэтому я использую для этого фабрики angular. Итак, мне нужно было получить данные один раз с сервера, и каждый раз, когда я получаю данные с помощью этой определенной службы. Прямо как глобальные переменные. Вот образец:

var myApp =  angular.module('myservices', []);

myApp.factory('myService', function($http) {
    $http({method:"GET", url:"/my/url"}).success(function(result){
        return result;
    });
});

В моем контроллере я использую эту службу как:

function myFunction($scope, myService) {
    $scope.data = myService;
    console.log("data.name"+$scope.data.name);
}

Он отлично работает для меня в соответствии с моими требованиями. Но проблема в том, что когда я перезагружаю свою веб-страницу, служба снова вызывается и запрашивает сервер. Если в промежутке выполняется какая-то другая функция, которая зависит от «определенной службы», она выдает ошибку типа «что-то не определено». Поэтому я хочу подождать в своем скрипте, пока служба не загрузится. Как я могу это сделать? Есть ли способ сделать это в angularjs?

анилCSE
источник

Ответы:

150

Вы должны использовать обещания для асинхронных операций, когда вы не знаете, когда они будут выполнены. Обещание «представляет собой операцию, которая еще не завершена, но ожидается в будущем». ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Пример реализации будет таким:

myApp.factory('myService', function($http) {

    var getData = function() {

        // Angular $http() and then() both return promises themselves 
        return $http({method:"GET", url:"/my/url"}).then(function(result){

            // What we return here is the data that will be accessible 
            // to us after the promise resolves
            return result.data;
        });
    };


    return { getData: getData };
});


function myFunction($scope, myService) {
    var myDataPromise = myService.getData();
    myDataPromise.then(function(result) {  

       // this is only run after getData() resolves
       $scope.data = result;
       console.log("data.name"+$scope.data.name);
    });
}

Изменить: Что касается комментария Sujoys, что мне нужно сделать, чтобы вызов myFuction () не возвращался, пока функция .then () не завершит выполнение.

function myFunction($scope, myService) { 
    var myDataPromise = myService.getData(); 
    myDataPromise.then(function(result) { 
         $scope.data = result; 
         console.log("data.name"+$scope.data.name); 
    }); 
    console.log("This will get printed before data.name inside then. And I don't want that."); 
 }

Что ж, предположим, что вызов getData () занял 10 секунд. Если бы функция за это время ничего не вернула, она превратилась бы в обычный синхронный код и зависала бы в браузере до завершения.

Хотя обещание возвращается мгновенно, браузер тем временем может продолжить работу с другим кодом. Как только обещание разрешается / не выполняется, запускается вызов then (). Таким образом, это имеет гораздо больше смысла, даже если это может немного усложнить поток вашего кода (в конце концов, сложность - это распространенная проблема асинхронного / параллельного программирования!)

Микель
источник
2
Это решило мою проблему !!! Для всех остальных у меня был раскрывающийся список, в котором требовались данные из вызова ajax, поэтому при создании области данные были недоступны. С этой отсрочкой можно назначить область действия, чтобы данные поступали из вызова ajax.
Кэт Лим Руис
8
@mikel: У меня есть еще один вопрос. Ваш вызов myFuction () вернется немедленно, но это обещание .then () вызовет позже. Что мне нужно сделать, чтобы вызов myFuction () не возвращался, пока функция .then () не завершит выполнение. function myFunction($scope, myService) { var myDataPromise = myService.getData(); myDataPromise.then(function(result) { $scope.data = result; console.log("data.name"+$scope.data.name); }); console.log("This will get printed before data.name inside then. And I don't want that."); }
Суджой
13

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

К вашим услугам:

.factory('DataHandler',function ($http){

   var GetRandomArtists = function(data, callback){
     $http.post(URL, data).success(function (response) {
         callback(response);
      });
   } 
})

В вашем контроллере:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });
Рауль Гомес
источник
Отличное решение. Я думал об этом же, когда изучал это. Рад, что кто-то это выложил.
Nate
0

К вашему сведению, здесь используется Angularfire, поэтому он может немного отличаться для другой службы или другого использования, но должен решить тот же isse $ http. У меня была та же проблема, единственное решение, которое мне подходило, лучше всего заключалось в объединении всех сервисов / фабрик в одно обещание в области. На каждом маршруте / представлении, которые требовали загрузки этих сервисов и т. Д., Я помещаю любые функции, требующие загруженных данных, внутри функции контроллера, т.е. myfunct () и основной app.js при запуске после аутентификации.

myservice.$loaded().then(function() {$rootScope.myservice = myservice;});

и в представлении я только что сделал

ng-if="myservice" ng-init="somevar=myfunct()"

в первом / родительском элементе представления / оболочке, чтобы контроллер мог запускать все внутри

myfunct()

не беспокоясь о проблемах с асинхронными обещаниями / порядком / очередью. Я надеюсь, что это поможет кому-то с теми же проблемами, что и я.

Сэмюэл Барни
источник
0

У меня была такая же проблема, и ничего, если бы они работали для меня. Вот что сработало ...

app.factory('myService', function($http) {
    var data = function (value) {
            return $http.get(value);
    }

    return { data: data }
});

а затем функция, которая его использует ...

vm.search = function(value) {

        var recieved_data = myService.data(value);

        recieved_data.then(
            function(fulfillment){
                vm.tags = fulfillment.data;
            }, function(){
                console.log("Server did not send tag data.");
        });
    };

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

user3127557
источник