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

93

J'utilise des données provenant d'un service RESTful sur plusieurs pages. J'utilise donc des usines angulaires pour cela. Donc, je devais obtenir les données une fois du serveur, et chaque fois que j'obtiens les données avec ce service défini. Tout comme une variable globale. 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 en tant que:

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

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

anilCSE
la source

Réponses:

150

Vous devez utiliser des 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 est attendue dans le futur". ( https://developer.mozilla.org/en/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: Concernant Sujoys commentez que Que dois-je faire pour que l'appel myFuction () ne revienne pas tant que la fonction .then () n'a pas terminé l'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."); 
 }

Eh bien, supposons que l'appel à getData () prenne 10 secondes pour se terminer. Si la fonction ne retournait rien pendant cette période, elle deviendrait effectivement un code synchrone normal et suspendrait le navigateur jusqu'à ce qu'il soit terminé.

Cependant, la promesse revenant instantanément, le navigateur est libre de continuer avec un autre code entre-temps. Une fois la promesse résolue / échouée, l'appel then () est déclenché. Cela a donc beaucoup plus de sens 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!)

mikel
la source
2
Cela a résolu mon problème !!! Pour n'importe qui d'autre, j'avais une liste déroulante qui avait besoin des données de l'appel ajax, donc lorsque la portée a été créée, les données n'étaient pas disponibles. Avec ce report, la portée peut être assignée pour avoir les données provenant de l'appel ajax.
Kat Lim Ruiz
8
@mikel: J'ai une autre question ici. Votre appel myFuction () reviendra immédiatement mais cette promesse .then () sera appelée plus tard. Que dois-je faire pour que l'appel myFuction () ne revienne pas tant que la fonction .then () n'a pas terminé l'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."); }
Sujoy
13

pour les nouveaux utilisateurs, 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;
   });
Raul Gomez
la source
Excellente solution. Je pensais dans le même sens quand je l'étais. Heureux que quelqu'un ait mis ça là-bas.
Nate
0

Pour info, cela utilise 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 ce même problème, la seule solution qui me convenait le mieux était de combiner tous les services / usines en une seule promesse sur la portée. Sur chaque route / vue qui nécessitait le chargement de ces services / etc, je mets toutes les fonctions qui nécessitent des données chargées dans la fonction du contrôleur, c'est-à-dire myfunct () et l'application principale.js en cours d'exécution après l'authentification je mets

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 / élément de vue parent / wrapper pour que le contrôleur puisse tout exécuter à l'intérieur

myfunct()

sans vous soucier des promesses / commandes / problèmes de file d'attente asynchrones. J'espère que cela aide quelqu'un avec les mêmes problèmes que moi.

Samuel Barney
la source
0

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

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 si nécessaire mais je pense que c'est une bonne pratique pour l'extensibilité. La plupart de ce dont vous aurez besoin pour un testament pour un autre, en particulier lors de l'utilisation d'API. Quoi qu'il en soit, j'espère que cela a été utile.

user3127557
la source