Wie kann ich warten, bis die Antwort von der $ http-Anfrage in anglejs kommt?

93

Ich verwende einige Daten, die von einem RESTful-Dienst stammen, auf mehreren Seiten. Dafür benutze ich eckige Fabriken. Daher musste ich die Daten einmal vom Server abrufen und jedes Mal, wenn ich die Daten mit diesem definierten Dienst abrufe. Genau wie eine globale Variable. Hier ist das Beispiel:

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

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

In meinem Controller verwende ich diesen Dienst als:

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

Es funktioniert gut für mich gemäß meinen Anforderungen. Das Problem hierbei ist jedoch, dass beim erneuten Laden meiner Webseite der Dienst erneut aufgerufen wird und Server angefordert werden. Wenn zwischenzeitlich eine andere Funktion ausgeführt wird, die vom "definierten Dienst" abhängt, wird der Fehler ausgegeben, als ob "etwas" undefiniert ist. Ich möchte also in meinem Skript warten, bis der Dienst geladen ist. Wie kann ich das machen? Gibt es das überhaupt in Angularjs?

anilCSE
quelle

Antworten:

150

Sie sollten Versprechen für asynchrone Vorgänge verwenden, bei denen Sie nicht wissen, wann sie abgeschlossen sein werden. Ein Versprechen "stellt eine Operation dar, die noch nicht abgeschlossen ist, aber in Zukunft erwartet wird." ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

Eine beispielhafte Implementierung wäre wie folgt:

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

Bearbeiten: In Bezug auf Sujoys Kommentar: Was muss ich tun, damit der Aufruf von myFuction () erst zurückkehrt, wenn die Funktion .then () die Ausführung beendet hat?

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

Nehmen wir an, der Aufruf von getData () dauerte 10 Sekunden. Wenn die Funktion in dieser Zeit nichts zurückgeben würde, würde sie effektiv zu normalem synchronem Code werden und den Browser hängen lassen, bis er abgeschlossen ist.

Da das Versprechen jedoch sofort zurückkehrt, kann der Browser in der Zwischenzeit mit anderem Code fortfahren. Sobald das Versprechen aufgelöst ist / fehlschlägt, wird der Aufruf then () ausgelöst. Auf diese Weise ist dies viel sinnvoller, auch wenn dadurch der Codefluss möglicherweise etwas komplexer wird (Komplexität ist schließlich ein häufiges Problem der asynchronen / parallelen Programmierung im Allgemeinen!).

mikel
quelle
2
Dies hat mein Problem gelöst !!! Für alle anderen hatte ich ein Dropdown-Menü, in dem Daten aus einem Ajax-Aufruf benötigt wurden. Als der Bereich erstellt wurde, waren die Daten nicht verfügbar. Mit dieser Verschiebung kann dem Bereich zugewiesen werden, dass die Daten aus dem Ajax-Aufruf stammen.
Kat Lim Ruiz
8
@mikel: Ich habe hier noch eine Frage. Ihr Aufruf von myFuction () wird sofort zurückgegeben, aber dieses Versprechen .then () wird später aufgerufen. Was muss ich tun, damit der Aufruf von myFuction () erst zurückkehrt, wenn die Funktion .then () die Ausführung beendet hat? 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

Für Neulinge können Sie auch einen Rückruf verwenden, zum Beispiel:

In Ihrem Dienst:

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

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

In Ihrem Controller:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });
Raul Gomez
quelle
Tolle Lösung. Ich habe in die gleiche Richtung gedacht, als ich mich damit befasst habe. Ich bin froh, dass jemand das hier veröffentlicht hat.
Nate
0

Zu Ihrer Information, dies verwendet Angularfire, so dass es für einen anderen Dienst oder eine andere Verwendung etwas variieren kann, aber das gleiche Problem lösen sollte, das $ http hat. Ich hatte die gleiche Problemlösung, die für mich am besten geeignet war, alle Dienstleistungen / Fabriken in einem einzigen Versprechen über den Umfang zu kombinieren. Auf jeder Route / Ansicht, auf der diese Dienste / usw. geladen werden mussten, habe ich alle Funktionen, die geladene Daten erfordern, in die Controller-Funktion eingefügt, z. B. myfunct () und die Haupt-App.js, die nach der Authentifizierung ausgeführt werden

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

und in der Ansicht habe ich gerade getan

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

im ersten / übergeordneten Ansichtselement / Wrapper, damit der Controller alles darin ausführen kann

myfunct()

ohne sich um asynchrone Versprechen / Bestell- / Warteschlangenprobleme zu kümmern. Ich hoffe, das hilft jemandem mit den gleichen Problemen, die ich hatte.

Samuel Barney
quelle
0

Ich hatte das gleiche Problem und keines, wenn diese für mich funktionierten. Hier ist, was funktioniert hat ...

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

    return { data: data }
});

und dann ist die Funktion, die es verwendet, ...

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

Der Service ist nicht so notwendig, aber ich denke, es ist eine gute Praxis für die Erweiterbarkeit. Das meiste, was Sie für einen benötigen, wird für jeden anderen verwendet, insbesondere wenn Sie APIs verwenden. Jedenfalls hoffe ich, dass dies hilfreich war.

user3127557
quelle