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

AngularjsWaitAngularjs Service

Angularjs Problem Overview


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?

Angularjs Solutions


Solution 1 - Angularjs

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!)

Solution 2 - Angularjs

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

Solution 3 - Angularjs

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.

Solution 4 - Angularjs

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanilCSEView Question on Stackoverflow
Solution 1 - AngularjsMichael LowView Answer on Stackoverflow
Solution 2 - AngularjsRaul GomezView Answer on Stackoverflow
Solution 3 - Angularjsuser3127557View Answer on Stackoverflow
Solution 4 - AngularjsSamuel BarneyView Answer on Stackoverflow