How to wait till the response comes from the $http request, in angularjs?
Asked Answered
W

4

94

I am using some data which is from a RESTful service in multiple pages. So I am using angular factories for that. So, I required to get the data once from the server, and everytime I am getting the data with that defined service. Just like a global variables. Here is the sample:

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

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

In my controller I am using this service as:

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

Its working fine for me as per my requirements. But the problem here is, when I reloaded in my webpage the service will gets called again and requests for server. If in between some other function executes which is dependent on the "defined service", It's giving the error like "something" is undefined. So I want to wait in my script till the service is loaded. How can I do that? Is there anyway do that in angularjs?

Wylma answered 24/8, 2013 at 18:35 Comment(0)
M
150

You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)

An example implementation would be like:

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: Regarding Sujoys comment that What do I need to do so that myFuction() call won't return till .then() function finishes execution.

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

Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.

With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)

Mayo answered 24/8, 2013 at 19:1 Comment(2)
This solved my issue!!! For anyone else, I had a dropdown that needed data from ajax call, so when the scope was created, the data was not available. With this deferral, the scope can be assigned to have the data coming from the ajax call.Raasch
@mikel : I have another question here. Your myFuction() call will return immediately but that promise .then() will call later. What do I need to do so that myFuction() call won't return till .then() function finishes execution. 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."); }Raymundorayna
F
13

for people new to this you can also use a callback for example:

In your service:

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

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

In your controller:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });
Foretime answered 6/7, 2014 at 2:59 Comment(1)
Great solution. Was thinking along this same line when I was looking into it. Glad someone put this out there.Cantata
C
1

I was having the same problem and none if these worked for me. Here is what did work though...

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

    return { data: data }
});

and then the function that uses it is...

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

The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.

Carnot answered 6/7, 2015 at 23:25 Comment(0)
T
0

FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put

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

and in the view I just did

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

in the first/parent view element/wrapper so the controller can run everything inside

myfunct()

without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.

Titanic answered 11/11, 2015 at 22:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.