1

app.get('/zones/:id/experiences', function(req,res) {
  var zone_key = req.params.id;
  var recent = [];
  var ref = firebase.database().ref('participants/'+zone_key+'/experiences');
  ref.on("value", function(snapshot) {
    snapshot.forEach((snap) => {
      firebase.database().ref('experiences').child(snap.val()).once("value").then((usersnap) => {
        recent.push(usersnap.val());
      });
    });
  console.log(recent);
  });
  res.render('experiences',{key: zone_key, list: recent});  
});

In the above code, I am querying a reference point to get a set of "keys". Then for each key, I am querying another reference point to get the object associated to that key. Then for each object returned for those keys, I simply want to push the objects into a list. I then want to pass in this list to the client site to do stuff with the data using the render.

For some reason, the recent [] never gets populated. It remains empty. Is this an issue with my variables not being in scope? I console logged to check what the data the reference points are returning and its all good, I get the data that I want.

P.S is nesting queries like this ok? For loop within another query

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
user3892254
  • 139
  • 2
  • 15
  • The callback passed to `on` is invoked asynchronously: https://stackoverflow.com/q/14220321/6680611 – cartant Nov 04 '17 at 03:53

1 Answers1

1

As cartant commented: the data is loaded from Firebase asynchronously; by the time you call res.render, the list will still be empty.

An easy way to see this is the 1-2-3 test:

app.get('/zones/:id/experiences', function(req,res) {
  var zone_key = req.params.id;
  var recent = [];
  var ref = firebase.database().ref('participants/'+zone_key+'/experiences');
  console.log("1");
  ref.on("value", function(snapshot) {
    console.log("2");
  });
  console.log("3");
});

When you run this code, it prints:

1

3

2

You probably expected it to print 1,2,3, but since the on("value" loads data asynchronously, that is not the case.

The solution is to move the code that needs access to the data into the callback, where the data is available. In your code you need both the original value and the joined usersnap values, so it requires a bit of work.

app.get('/zones/:id/experiences', function(req,res) {
  var zone_key = req.params.id;
  var recent = [];
  var ref = firebase.database().ref('participants/'+zone_key+'/experiences');
  ref.on("value", function(snapshot) {
    var promises = [];
    snapshot.forEach((snap) => {
      promises.push(firebase.database().ref('experiences').child(snap.val()).once("value"));
      Promise.all(promises).then((snapshots) => {
        snapshots.forEach((usersnap) => {
          recent.push(usersnap.val());
        }); 
        res.render('experiences',{key: zone_key, list: recent});  
      });
    });
  });
});

In this snippet we use Promise.all to wait for all usersnaps to load.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • In that case it seems like you're trying to use non-existing data. It's hard for me to be certain on this, since I copied that part from your own answer and don't have your data available to verify. Can you try and reproduce the problem in a jsbin, so I can have to look? – Frank van Puffelen Nov 04 '17 at 19:08
  • Yes you were right, I managed to fix that portion. However, that "recent" list still isn't populating. I console logged whats happening in the snapshots.forEach((usersnap) => { ... }) and I can print out each object that was added to my Promises list but in terminal the code doesn't console log anything after that block of code. Its like it is still trying to read more data from the list even after I have printed all the contents. – user3892254 Nov 04 '17 at 20:57
  • That depends on where you `console.log()` the data. My first snippet shows that if you print it in location `3`, the data will be empty. There is nothing you can do about that. That's why I moved the code that needs the data **into** the callback. – Frank van Puffelen Nov 05 '17 at 06:33