94 votes

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

J'utilise des données provenant d'un service RESTful dans plusieurs pages. J'utilise donc des fabriques angulaires pour cela. Ainsi, je dois obtenir les données une fois du serveur, et chaque fois que je reçois les données avec ce service défini. Tout comme les variables globales. Voici l'exemple :

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 comme.. :

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

Cela fonctionne bien pour moi, conformément à mes exigences. Mais le problème est que, lorsque je recharge ma page web, le service est appelé à nouveau et demande le serveur. Si entre temps une autre fonction s'exécute qui dépend du "service défini", elle donne une erreur comme "quelque chose" est indéfini. Donc je veux attendre dans mon script jusqu'à ce que le service soit chargé. Comment puis-je faire cela ? Existe-t-il un moyen de le faire dans Angularjs ?

150voto

mikel Points 10793

Vous devriez utiliser les promesses pour les opérations asynchrones dont 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 est attendue dans le futur". ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Un exemple de mise en œuvre serait le suivant :

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 : Concernant le commentaire de Sujoys Que dois-je faire pour que l'appel à myFuction() ne revienne pas avant que la fonction .then() ne termine son exécution.

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 pour aboutir. Si la fonction ne renvoie rien pendant ce temps, elle devient un code synchrone normal et bloque le navigateur jusqu'à ce qu'elle soit terminée.

Mais comme la promesse revient instantanément, le navigateur est libre de continuer à utiliser d'autres codes pendant ce temps. Une fois la promesse résolue/échouée, l'appel then() est déclenché. C'est donc beaucoup plus logique de cette façon, même si cela peut rendre le flux de votre code un peu plus complexe (la complexité est un problème courant de la programmation asynchrone/parallèle en général, après tout !)

13voto

Raul Gomez Points 591

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

1voto

user3127557 Points 11

J'avais le même problème et aucune de ces solutions n'a fonctionné pour moi. Voici ce qui a fonctionné par contre...

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

    return { data: data }
});

et ensuite 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 si nécessaire mais je pense que c'est une bonne pratique pour l'extensibilité. La plupart de ce dont vous aurez besoin pour l'un sera pour n'importe quel autre, surtout lorsque vous utilisez des API. En tout cas, j'espère que cela a été utile.

0voto

Samuel Barney Points 3

Pour information, ceci est fait en utilisant Angularfire donc cela peut varier un peu pour un service différent ou une autre utilisation mais devrait résoudre le même problème que $http. J'ai eu le même problème, mais la seule solution qui me convenait le mieux était de combiner tous les services/usines en une seule promesse sur le scope. Sur chaque route/vue qui avait besoin que ces services/etc soient chargés, j'ai mis toutes les fonctions qui nécessitent des données chargées à l'intérieur de la fonction du contrôleur, par exemple myfunct() et dans le fichier principal app.js, lors de l'exécution après authentification, 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/parent élément de vue/wrapper pour que le contrôleur puisse tout exécuter à l'intérieur.

myfunct()

sans se soucier des problèmes de promesses asynchrones, d'ordre et de file d'attente. J'espère que cela aidera quelqu'un qui a les mêmes problèmes que moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X