Annie Zhou
Annie Zhou

Reputation: 33

returning array after finish reading from Firebase

I want to push all values that satisfy a certain condition to an array then return this array. Here's my code

exports.getNearbyPosts = function (myLat, myLng){
  var query = firebase.database().ref("posts/").orderByKey();
  var listOfItems = [];
  query.once("value")
    .then(function(snapshot) {
      snapshot.forEach(function(childSnapshot) {
        var key = childSnapshot.key;
        var childData = childSnapshot.val();
        var dist = getDistanceBetweenCoord(childData.lat, childData.lng, myLat, myLng);
        if(dist < 10239820938502){
          listOfItems.push(childSnapshot);
        }
    });
  });

  return listOfItems;
}

However since forEach is asynchronous, and it seems like I cannot use a callback on forEach, the listOfItems I return would just be an empty array. I have also tried ".then", but it would only allow me to do more stuff inside ".then" and I would like to return this array as a variable to other parts of the code. How can I return the correct array after forEach has been executed?

Upvotes: 3

Views: 2110

Answers (1)

Devan Buggay
Devan Buggay

Reputation: 2759

It's not the forEach that is async (JavaScript, Node.js: is Array.forEach asynchronous?), it's the query.once("value"). You can solve this problem in a variety of ways. With callbacks, promises, or await/async.

I suggest reading this very informative answer that covers much more with better examples: How do I return the response from an asynchronous call?

With callbacks

The idea here is to provide a function that can be called with the result once the asynchronous work is done, but after our work in forEach is complete.

exports.getNearbyPosts = function (myLat, myLng, callback){
  var query = firebase.database().ref("posts/").orderByKey();
  var listOfItems = [];
  query.once("value").then(function(snapshot) {
    snapshot.forEach(function(childSnapshot) {
      var key = childSnapshot.key;
      var childData = childSnapshot.val();
      var dist = getDistanceBetweenCoord(childData.lat, childData.lng, myLat, myLng);
      if(dist < 10239820938502){
        listOfItems.push(childSnapshot);
      }
    });
    callback(listOfItems)
  });
}

getNearbyPosts(0, 0, (result) => {
  console.log(result);
});

With promises (ES2016)

Promises are another way to handle asynchronous code. I suggest reading about them here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises. It will explain them far better than I could.

Here is your code using promises:

exports.getNearbyPosts = function (myLat, myLng){
  var query = firebase.database().ref("posts/").orderByKey();
  var listOfItems = [];
  return new Promise((resolve, reject) => {
    query.once("value").then(function(snapshot) {
      snapshot.forEach(function(childSnapshot) {
        var key = childSnapshot.key;
        var childData = childSnapshot.val();
        var dist = getDistanceBetweenCoord(childData.lat, childData.lng, myLat, myLng);
        if(dist < 10239820938502){
          listOfItems.push(childSnapshot);
        }
      });
      resolve(listOfItems);
    });
  });
}

getNearbyPosts(0, 0).then(result => {
  console.log(result);
});

This can actually be simplified further as it appears query.once returns a promise itself. We can simply return the promise provided to us by query.once which will fire once after the .then of your internal function. We can chain them together like so.

exports.getNearbyPosts = function (myLat, myLng){
  var query = firebase.database().ref("posts/").orderByKey();
  var listOfItems = [];
  
  // We return the promise created by query.once
  return query.once("value").then((snapshot) => {
  
    // our inital .then will do some data filtering
    snapshot.forEach((childSnapshot) => {
      var key = childSnapshot.key;
      var childData = childSnapshot.val();
      var dist = getDistanceBetweenCoord(childData.lat, childData.lng, myLat, myLng);
      if(dist < 10239820938502){
        listOfItems.push(childSnapshot);
      }
    });
    
    // We return here to continue the chain. The next .then that picks this up will have this result
    return listOfItems;
  });
}

// Our last promise resolution in the chain, containing listOfItems
getNearbyPosts(0, 0).then(result => {
  console.log(result);
});

Upvotes: 8

Related Questions