1
votes

I've read many answers to this question but I just don't get it. Where does the promise go? I made a simple factory with an async call to a cloud database:

app.factory('asyncFactory', function() {

  let toController = function() {

    firebase.database().ref('en').once('value') // get the array from the cloud database
    .then(function(snapshot) { // take a snapshot
      console.log(snapshot.val()); // read the values from the snapshot
      return snapshot.val(); // this returns later
    });

    return 47 // this returns immeadiately
  };

  return {
    toController: toController // why is this necessary?
  }

});

I call it from my controller:

$scope.words = asyncFactory.toController();
console.log($scope.words);

Here's the response:

response

As you can see, 47 returns to the controller immediately. If I comment out return 47 then the factory returns undefined. Later the async data logs but doesn't return to the controller. I use promises every day but I can't figure out where the promise goes.

Second question: do I need the line toController: toController ? Can I get rid of it?

Thanks!

2
The promise doesn't go anywhere because instead of returning it, you're returning 47. Read blog.ninja-squad.com/2015/05/28/angularjs-promises. Regarding "do I need the line toController: toController", well, no, but then your service would have no method at all, and would be really useless. - JB Nizet

2 Answers

2
votes

To use the results from the firebase call in the controller, the factory method needs to return a promise:

app.factory('asyncFactory', function($q) {    
  return {
    toController: toController
  };

  function toController() {

    var es6promise = firebase.database().ref('en').once('value');

    var qPromise = $q.when(es6promise)
      .then(function(snapshot) { // take a snapshot
        console.log(snapshot.val()); // read the values from the snapshot
        return snapshot.val(); // this returns later
    });

    return qPromise;
  };

});

Because the firebase .once method returns an ES6 promise, that promise needs to be brought into the AngularJS framework by converting it to a $q Service promise with $q.when. Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.

In the controller, use the .then method to extract the data after it returns from the server:

var qPromise = asyncFactory.toController();

qPromise.then(function(data) {
    console.log(data)
    $scope.words = data;
});

The factory function immediately returns a promise. When the data arrives from the server, the data will be placed on $scope.

1
votes

Well toController is eating the promise for itself. ( whenever you call .then(), it means you are waiting for promise), Try this

app.factory('asyncFactory', function() {
  let toController = function() {
   var deferred = $q.defer();
    firebase.database().ref('en').once('value') // get the array from the cloud database
    .then(function(snapshot) { // take a snapshot
      console.log(snapshot.val()); // read the values from the snapshot
      return deferred.resolve(snapshot.val()); // this returns later
    });

    //return deferred.resolve(47) // this returns immeadiately
  };

  return {
    toController: toController // why is this necessary?
  }

});

If you don't want this line

return { toController: toController // why is this necessary? }

app.factory('asyncFactory', function() {

  return {
   var deferred = $q.defer();

    firebase.database().ref('en').once('value') // get the array from the cloud database
    .then(function(snapshot) { // take a snapshot
      console.log(snapshot.val()); // read the values from the snapshot
      return deferred.resolve(snapshot.val()); // this returns later
    });

    //return deferred.resolve(47) // this returns immeadiately
  };

})