web-dev-qa-db-fra.com

Comment attendre que la réponse vienne de la requête $ http, en angularjs?

J'utilise des données provenant d'un service RESTful sur plusieurs pages. J'utilise donc angular usines pour cela. Donc, je devais obtenir les données une fois du serveur, et chaque fois je reçois les données avec ce service défini. Juste comme une variable globale. Voici l'échantillon:

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

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

Dans mon contrôleur, j'utilise ce service en tant que:

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

Cela fonctionne bien pour moi selon mes exigences. Mais le problème, c’est que, lorsque je rechargerai ma page Web, le service sera rappelé et les demandes de serveur. Si entre les deux exécute une autre fonction qui dépend du "service défini", cela donne l'erreur comme "quelque chose" est indéfinie. Je veux donc attendre dans mon script que le service soit chargé. Comment puis je faire ça? Y at-il de toute façon faire cela dans angularjs?

88
anilCSE

Vous devez utiliser les promesses pour les opérations asynchrones où vous ne savez pas quand elles seront terminées. Une promesse "représente une opération qui n'est pas encore terminée, mais qui devrait se produire dans le futur". ( https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Un exemple d'implémentation serait comme:

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

Edit: À propos de Sujoy commenter Que dois-je faire pour que l'appel myFuction () ne retourne pas avant la fin de l'exécution de la fonction .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."); 
 }

Supposons que l'appel à getData () prenne 10 secondes. Si la fonction ne renvoyait rien dans ce délai, elle deviendrait effectivement du code synchrone normal et bloquerait le navigateur jusqu'à ce qu'il soit terminé.

Avec la promesse de revenir instantanément, le navigateur est libre de continuer avec d’autres codes en attendant. Une fois que la promesse a résolu/échoue, l'appel then () est déclenché. Cela semble donc beaucoup plus logique, même si cela peut rendre le flux de votre code un peu plus complexe (la complexité est un problème courant de programmation asynchrone/parallèle en général après tout!)

146
mikel

pour les personnes novices, vous pouvez également utiliser un rappel, par exemple:

A votre service:

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

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

Dans votre contrôleur:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });
13
Raul Gomez

Pour votre information, cela utilise Angularfire, donc cela peut varier un peu pour un service différent ou pour un autre usage, mais cela devrait résoudre le même problème que $ http a. J'avais cette même question, la seule solution qui me convenait le mieux était de combiner tous les services/usines en une seule promesse sur le périmètre. Sur chaque route/vue ayant besoin de ces services/etc pour être chargés, je mets toutes les fonctions qui nécessitent des données chargées à l'intérieur de la fonction de contrôleur, c'est-à-dire myfunct () et le principal app.js à exécuter après l'autorisation, j'ai mis

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

et dans la vue que je viens de faire

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

dans le premier élément de vue/parent/wrapper afin que le contrôleur puisse tout exécuter à l'intérieur

myfunct()

sans vous soucier des promesses asynchrones/ordre/files d'attente. J'espère que cela aidera quelqu'un qui a les mêmes problèmes que moi.

0
Samuel Barney

J'avais le même problème et aucun si cela fonctionnait pour moi. Voici ce qui a bien fonctionné ...

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

    return { data: data }
});

et puis la fonction qui l'utilise est ...

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

Le service n'est pas nécessaire, mais je pense que c'est une bonne pratique d'extensibilité. La plupart de ce dont vous aurez besoin pour un testament pour un autre, en particulier lorsque vous utilisez des API. Quoi qu'il en soit, j'espère que cela a été utile.

0
user3127557