Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing variable in parent scope to callback function

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;
};
like image 603
John Robinson Avatar asked Oct 10 '14 10:10

John Robinson


2 Answers

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.

  1. You register a listener for child_added that will call on_post_added for every post that is added to your Firebase
  2. This will result in a call to the server, which may take a significant amount of time to return
  3. Meanwhile your JavaScript code continues and...
  4. Logs the array, which at this stage is still empty
  5. And then thus returns an empty array
  6. Now at some point the server returns the new value(s) and your callback is invoked
  7. Which means we can add it to the array without problems
  8. 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
}
like image 109
Frank van Puffelen Avatar answered Nov 15 '22 03:11

Frank van Puffelen


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.
like image 22
MrCode Avatar answered Nov 15 '22 03:11

MrCode