17

Here is my code:

var userRef = new Firebase("https://awesome.firebaseio.com/users/");

var tokenRef = userRef.child(key+'/tokens');
tokenRef.once('value', function(snapshot){

  var userTokenSync = $firebase(tokenRef);
  var userTokens = userTokenSync.$asArray();


  console.log(userTokens);
  console.log(userTokens[0]);
        
  for(var i=0, len = userTokens.length; i < len; i++) {
     console.log(userTokens[i]);
  }
  console.log('done');
})

This code gets the tokens of a user from firebase, and I just want to browse the tokens array.

Here is what the console gives me:

enter image description here

As you can see, I cannot access the array. Do you have any idea of how I could do this?

Thanks in advance.

Community
  • 1
  • 1
Vico
  • 1,504
  • 1
  • 22
  • 50
  • See also http://stackoverflow.com/questions/4326350/how-do-i-wait-for-an-asynchronously-dispatched-block-to-finish – Kato Feb 09 '15 at 17:23

1 Answers1

23

Welcome to Asynchronous Loading 101.

  var userTokens = userTokenSync.$asArray();

  console.log(userTokens);
  console.log(userTokens[0]);

  for(var i=0, len = userTokens.length; i < len; i++) {
     console.log(userTokens[i]);
  }
  console.log('done');

The first line of this snippets starts loading your data from Firebase. But that loading might take some time. And since the browser doesn't want to block things, it continues executing the script.

By the time the browser executes your console.log(userTokens); the data most likely hasn't been loaded yet. So it just prints the object to the console as a placeholder.

By the time it gets to the for loop, the data may or may not yet have been loaded from Firebase.

At some point you clicked the arrow next to the logged userTokens. By that time the data had loaded from Firebase and the console shows the latest data.

The solution is provided by AngularFire, in the form of a promise. AngularFire promises to at some point in the future have the data for you. If you have a piece of code that should only execute when the data is loaded, you can write it like this:

  var userTokens = userTokenSync.$asArray();

  console.log(userTokens);
  console.log(userTokens[0]);

  userTokens.$loaded(function(userTokens) {
    for(var i=0, len = userTokens.length; i < len; i++) {
      console.log(userTokens[i]);
    }
    console.log('done');
  });

  console.log('at last line, but not done yet');

Update

Note that the above is a bad example of when to use $loaded. Since $loaded will fire only once, it will only log the initial contents of the array. If you're just trying to see if/when your data has loaded, the idiomatic approach is to add the userTokens to the scope:

$scope.userTokens = userTokens;

And add this to your view/HTML:

<pre>{{ userTokens | json }}</pre>

AngularFire will now monitor when the userTokens are loaded or modified in any way and will tell AngularJS to update the view if that happens.

Also see these:

Yup... we get this question a lot.

Community
  • 1
  • 1
Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645