user2422960
user2422960

Reputation: 1526

AngularJS: Waiting for an asynchronous call

I can't wrap my head around AngularJS' concept of promises.

I have a provider:

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

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  $http.post('../sys/core/fetchPacking.php').then(function(promise){
                      var packings = promise.data;
                      return packings;
                  });
              }
           }
       }
   }
});

As you can see, this provides a method getPackings(), which will return an object

Now, if I use that in my main application to receive the data, the call will be asynchronous, resulting in an issue where I would have to 'wait' for the data:

var packings = packingProvider.getPackings();

console.log(packings); // undefined

How would i do this without refactoring the process into my main controller?

Upvotes: 11

Views: 30071

Answers (3)

Konstantin K
Konstantin K

Reputation: 1317

Try

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

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  return $http.post('../sys/core/fetchPacking.php').then(function(response){
                      return response.data; // packings
                  });
              }
           }
       }
   }
});

Then

packingProvider.getPackings().then(function(packings){
    console.log(packings);
});

Main idea: you need to return the promise object from the getPackings function. Not sure if you can make the call to it synchronous but it's almost definitely not a great idea to do so. Also, if you want to make "packings" a model object on your controller and use for a two-directional binding, you can safely assign the promise object, which Angular will resolve as soon as data comes through:

$scope.packings = packingProvider.getPackings();

UPDATE

As of 1.2.0-rc.3 promise unwrapping has been deprecated (https://github.com/angular/angular.js/blob/master/CHANGELOG.md#120-rc3-ferocious-twitch-2013-10-14) , so the line of code above will not result in a two-directional binding any more.

Upvotes: 7

SgtPooki
SgtPooki

Reputation: 11640

There are a ton of resources available that show you how to use Deferreds/Promises with jQuery. Try a search for those and that may help you get over this hurdle. Afaik, the AngularJS promises are the same as the jQuery promises.

A jQuery promise is used something like this:

var getPackings = function() { return $.get('../sys/core/fetchPacking.php'); };
var packings;
$.when(getPackings()).then(function(data){ packings = data; console.log(packings) });

Keep in mind though that jQuery ajax calls have functions such as .done, .success, etc.. that would replace the generic Deferreds' .when().then() functions.

In the above jQuery method, you can see that we have to set the data and output it in the .then() function, because you cannot guarantee that the asynchronous process is done anywhere else. So ideally you would call to whatever function continues your processing in the .then() function, like so:

$.when(myAsyncFunc()).then(myAppCanContinue(dataReturnedByAsyncFunc));

Angular is going to follow the same logic. If you understand the above, it should be easier to understand how to accomplish this in Angular. Also, check out this article that gives simple examples: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

In order to get your code to work, you will need to do something like this:

packingProvider.provider('packingProvider',function(){
    return{
       $get: function($http){
           return{
              getPackings: function(){
                  return $http.post('../sys/core/fetchPacking.php');
              }
           }
       }
   }
});
var packings = packingProvider.getPackings();

packings.then(function(data) { console.log(data)});

Upvotes: 1

drhayes
drhayes

Reputation: 581

The return value from within your then method isn't returning when you think it is; it's returning later.

In the statement var packings = packingProvider.getPackings(); the return value is undefined because the promise returned from $http is asynchronous. That means that the call to $http.post happens, does not complete, then your function returns. In JS functions that don't return anything return undefined. The post call completes later and executes return packings; which gets returned to nowhere.

The getPackings method should probably return the promise from $http.post directly. That way any code that wants to use this method can call then on the promise directly and set the value the way it needs to. In a controller you could assign that promise directly to the $scope and use it in your view. Here's a nice post that explains that particular feature: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

Incidentally, it looks like you've got a long-winded service declaration there; any reason not to shorten it to something like this?

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

module.service('packing', function($http) {
  return {
    getPackings: function() {
      return $http.post('../sys/core/fetchPacking.php');
    }
  };
});

I'm relatively new to AngularJS but I don't see any gain in all that typing. ( =

Upvotes: 7

Related Questions