3

I have this piece of code using on() to get data from Firebase, inside on() I create object which I want to send out of function for future use - using return, but it seems it doesn't return anything. So question is how can I make it right?

postsRef.on('value', function(snapshot) {
 if (snapshot.val() === null) {
  var allPosts = false,
  numberOfPosts = 0;
 }
 else {
  var allPosts = snapshot.val(),
  numberOfPosts = Object.size(allPosts);
 }
 var postsData = {
  content: allPosts,
  count: numberOfPosts
 };
 return postsData;
});
Johncze
  • 469
  • 1
  • 6
  • 19

1 Answers1

6

The callback function is called asynchronously (some time in the future). So by the time it is invoked, postsRef.on(...) has already returned and any code immediately after it will have run.

For example, this might be tempting, but would not work:

var postsData;
postsRef.on('value', function(snapshot) {
    postsData = snapshot.val();
});
console.log(postsData); // postsData hasn't been set yet!

So there are a few different ways to tackle this. The best answer will depend on preference and code structure:

Move the logic accessing postsData into the callback

postsRef.on('value', function(snapshot) {
    postsData = snapshot.val();
    console.log(postsData);
});

Call another function when the callback is invoked

function logResults(postsData) {
   console.log(postsData);
}

postsRef.on('value', function(snapshot) {
    logResults(snapshot.val());
});

Trigger an event

function Observable() {
   this.listeners = [];
}

Observable.prototype = {
   monitorValue: function( postsRef ) {
      var self = this;
      postsRef.on('value', function(snapshot) {
         self._notifyListeners(postsRef);
      });
   },

   listen:  function( callback ) {
      this.listeners.push(callback);
   },

   _notifyListeners: function(data) {
      this.listeners.forEach(function(cb) {
         cb(data);
      }
   }
};

function logEvent( data ) {
   console.log(data);
}

var observable = new Observable();
observable.listen( logEvent );
observable.monitorValue( /* postsRef goes here */ );
Kato
  • 40,352
  • 6
  • 119
  • 149
  • Thank you for answer, I am still a little bit confused. When I move logic inside callback it is triggered many times - during every change of ref so I use once() now. Best for me would be get data out of it in returning object, maybe something like is mentioned here: http://stackoverflow.com/questions/11636731/handling-asynchronous-calls-firebase-in-functions with using promise() function in jQuery, but thank you anyway :) – Johncze Sep 30 '13 at 19:45
  • 1
    If you only want to be notified of the data value right now, and never in the future when it changes, then once() is the way to go! There is no such thing as a "returning object" in the asynchronous world, because there's no way to know when the callback will actually be invoked relative to the code that follows it. Promises are also a great choice, if they make more sense to you. – Kato Sep 30 '13 at 19:50