Come aspettare che la risposta arrivi dalla richiesta $ http, in angularjs?


93

Sto utilizzando alcuni dati provenienti da un servizio RESTful in più pagine. Quindi sto usando le fabbriche angolari per questo. Quindi, ho richiesto di ottenere i dati una volta dal server e ogni volta che ricevo i dati con quel servizio definito. Proprio come una variabile globale. Ecco il campione:

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

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

Nel mio controller utilizzo questo servizio come:

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

Funziona bene per me secondo le mie esigenze. Ma il problema qui è che quando ho ricaricato la mia pagina web il servizio verrà chiamato di nuovo e le richieste per il server. Se tra qualche altra funzione viene eseguita che dipende dal "servizio definito", viene visualizzato l'errore come "qualcosa" non è definito. Quindi voglio aspettare nel mio script finché il servizio non viene caricato. Come lo posso fare? C'è comunque farlo in angularjs?

Risposte:


150

Dovresti usare le promesse per le operazioni asincrone in cui non sai quando sarà completato. Una promessa "rappresenta un'operazione che non è stata ancora completata, ma prevista per il futuro". ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Un esempio di implementazione potrebbe essere:

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

Modifica: per quanto riguarda Sujoys, commenta cosa devo fare in modo che la chiamata myFuction () non ritorni fino a quando la funzione .then () non termina l'esecuzione.

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

Bene, supponiamo che la chiamata a getData () abbia impiegato 10 secondi per essere completata. Se la funzione non restituisse nulla in quel lasso di tempo, diventerebbe effettivamente un normale codice sincrono e bloccherebbe il browser fino al completamento.

Con la promessa che ritorna istantaneamente, però, il browser è libero di continuare con altro codice nel frattempo. Una volta che la promessa si risolve / fallisce, viene attivata la chiamata then (). Quindi ha molto più senso in questo modo, anche se potrebbe rendere il flusso del codice un po 'più complesso (la complessità è un problema comune della programmazione asincrona / parallela in generale, dopotutto!)


2
Questo ha risolto il mio problema !!! Per chiunque altro, avevo un menu a discesa che necessitava di dati da una chiamata ajax, quindi quando è stato creato l'ambito, i dati non erano disponibili. Con questo differimento, è possibile assegnare l'ambito per avere i dati provenienti dalla chiamata ajax.
Kat Lim Ruiz

8
@mikel: ho un'altra domanda qui. La tua chiamata myFuction () tornerà immediatamente, ma quella promessa .then () chiamerà più tardi. Cosa devo fare in modo che la chiamata myFuction () non ritorni fino a quando la funzione .then () non termina l'esecuzione. 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

per le persone nuove a questo puoi anche usare una richiamata, ad esempio:

Al tuo servizio:

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

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

Nel tuo controller:

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

Ottima soluzione. Stavo pensando sulla stessa linea quando ci stavo studiando. Sono contento che qualcuno abbia messo questo là fuori.
Nate

0

Cordiali saluti, questo utilizza Angularfire, quindi potrebbe variare leggermente per un servizio diverso o un altro utilizzo, ma dovrebbe risolvere lo stesso problema di $ http. Ho avuto questo stesso problema, l'unica soluzione che si adattava meglio a me era combinare tutti i servizi / fabbriche in un'unica promessa sull'ambito. Su ogni percorso / vista che richiedeva il caricamento di questi servizi / ecc.Metto tutte le funzioni che richiedono dati caricati all'interno della funzione del controller, ad esempio myfunct () e l'app.js principale in esecuzione dopo l'autenticazione che ho inserito

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

e nella vista l'ho appena fatto

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

nel primo elemento / wrapper / visualizzazione genitore in modo che il controller possa eseguire tutto all'interno

myfunct()

senza preoccuparsi delle promesse asincrone / ordine / problemi di coda. Spero che questo aiuti qualcuno con gli stessi problemi che ho avuto io.


0

Stavo avendo lo stesso problema e nessuno se questi funzionavano per me. Ecco cosa ha funzionato però ...

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

    return { data: data }
});

e poi la funzione che lo utilizza è ...

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

Il servizio non è necessario ma penso che sia una buona pratica per l'estensibilità. La maggior parte di ciò di cui avrai bisogno per uno sarà per qualsiasi altro, specialmente quando usi le API. Comunque spero che questo sia stato utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.