1

I'm using firebase functions with JavaScript to make a query about Firebase realtime, which will be used in an Android application. The main query obtains a collection of values that I then go through, and for each of them I launch another query. The results obtained from the second query stores in an array that is the one that I return as a result.

The problem is that the array that I return as an answer is empty, since the response is returned before the subqueries that add the data to the array end, that is, the problem I think is due to the asynchronous nature of the calls.

I have tried to reorganize the code in several ways and use promises to try that the result is not sent until all the queries have been made but the same problem is still happening.

The structure of the JSON database that I consult is the following:

"users" : {
  "uidUser" : {
    "friends" : {
      "uidUserFriend" : "mail"
    },
    "name" : "nameUser",
     ...
  },

  "uidUser2" : {
    "friends" : {
      "uidUserFriend" : "mail"
    },
    "name" : "nameUser",
     ...
  }

}

The functions are:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.searchFriends = functions.https.onCall((data,context) => {
  const db = admin.database();
  const uidUser = data.uidUser;
  var arrayOfResults = new Array();

  const refFriends = db.ref('/users/'+uidUser+'/friends');
  let user;

  return refFriends.once('value').then((snapshot) => {
    snapshot.forEach(function(userSnapshot){
      user = findUser(userSnapshot.key);
      arrayOfResults.push(user);
    });
    return { 
      users: arrayOfResults
    };
  }).catch((error) => {
    throw new functions.https.HttpsError('unknown', error.message, error);
  });

});

function findUser(uid){
  const db = admin.database();
  const ref = db.ref('/users/'+uid);
  return ref.once('value').then((snapshot) => {
    console.log(snapshot.key,snapshot.val());
    return snapshot.val();
  }).catch((error) => {
    console.log("Error in the query - "+error);
  });
}

I do not know if the problem is because I do not manage the promises well or because I have to orient the code in another way. Thank you.

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121

1 Answers1

1

Indeed, as you mentioned, you should "manage the promises" differently. Since you are triggering several asynchronous operations in parallel (with the once() method, which returns a promise) you have to use Promise.all().

The following code should do the trick:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.searchFriends = functions.https.onCall((data,context) => {
  const db = admin.database();
  const uidUser = data.uidUser;
  var arrayOfPromises = new Array();

  const refFriends = db.ref('/users/' + uidUser + '/friends');
  let user;

  return refFriends.once('value').then(snapshot => {
    snapshot.forEach(userSnapshot => {
      user = findUser(userSnapshot.key);
      arrayOfPromises.push(user);
    });
    return Promise.all(arrayOfPromises);
  })
  .then(arrayOfResults => {
      return { 
      users: arrayOfResults
    }; 
  })
  .catch((error) => {
    throw new functions.https.HttpsError('unknown', error.message, error);
  });

});

function findUser(uid){
  const db = admin.database();
  const ref = db.ref('/users/' + uid);
  return ref.once('value').then(snapshot => {
    console.log(snapshot.key,snapshot.val());
    return snapshot.val();
  });
}

Note that I have modified the name of the first array to arrayOfPromises, which makes more sense IMHO.

Note also that you receive the results of Promise.all() in an array corresponding to the fulfillment values in the same order than the queries array, see: Promise.all: Order of resolved values

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121