This is more of a JavaScript Closure question than a Firebase question. In the following code, the Firebase callback isn't recognizing the variable myArr in the parent scope.
function show_fb() {
var myArr = [];
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', function(snapshot) {
var newPost = snapshot.val();
myArr.push(newPost.user);
console.log(myArr); // works
});
console.log(myArr); // doesn't work. myArr in the firebase.on callback is
// not altering myArr
return myArr;
};
The callback is recognizing/modifying myArr
perfectly fine. The problem is that when your "doesn't work"-labeled console.log(myArr)
executes, the callback hasn't fired yet.
Let's change your code a bit:
var myArr = [];
function show_fb() {
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', on_post_added); // steps 1-3
console.log(myArr); // step 4
return myArr; // step 5
};
function on_post_added(snapshot) { // step 6
var newPost = snapshot.val();
myArr.push(newPost.user); // step 7
console.log(myArr); // step 8
}
Now it might be a bit easier to see what's going on.
- You register a listener for
child_added
that will call on_post_added
for every post that is added to your Firebase
- This will result in a call to the server, which may take a significant amount of time to return
- Meanwhile your JavaScript code continues and...
- Logs the array, which at this stage is still empty
- And then thus returns an empty array
- Now at some point the server returns the new value(s) and your callback is invoked
- Which means we can add it to the array without problems
- And logging it to the console shows the expected values
Handling asynchronous code/callbacks like this takes some getting used to, but is crucial to working with Firebase or any other AJAX-like or event driven technology. Putting the callback's code into a separate function sometimes makes it a bit easier to see what's going on.
In the case of Firebase it may also help to realize that the event is called child_added
for a reason. It is called whenever a child is added to the Firebase, not just when you first register your callback. So minutes later when some other client adds a child, your callback will still fire, adding a new child to myArr
. At that stage the code in steps 4 and 5 above will long have executed and will not execute again.
The solution is simple: put anything that you want to do after a child is added into your callback:
var myArr = [];
function show_fb() {
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', on_post_added);
};
function on_post_added(snapshot) {
var newPost = snapshot.val();
myArr.push(newPost.user);
console.log(myArr);
// do whatever else you need to do for a new post
}
The child_added event is not immediately executed, therefore is not synchronous and you can't rely on it to have executed, before the log call at the end of your function.
The procedure is:
- Define myArr
- Instantiate Firebase
- Assign event handler for child_added
- Log the value of myArr
- Return myArr - end of function
- Now at some point after this, the child_added event is fired, which pushes to your array, but as you can see, your show_fb() function has already finished executing by this point.
If Firebase makes ajax call(it probably does), then callback function(snapshot){..} is called after return statement. So function show_fb always returns [].
for instance:
- You execute this statement: var x=show_fb();
- show_fb creates empty array
- function creates ajax call
- function returns myArr (it is empty at this moment)
- variable x gets reference to myArr (array is still empty)
- callback is called and inserts new value to x (x and myArr have same instance)