Search code examples
javascriptfirebasefirebase-realtime-databasees6-promise

Firebase multiple async requests returned via Promises


I have a Firebase function that is attempting to take an array of UIDs and return an array of User Objects. I am trying to use Promise.all() to return all of the async results, but I am getting an empty array returned. I am, however, getting the logged out results after the fact.

const fetchUserObjects = function(uids){
  let promises = []
  uids.forEach((uid) => {
    admin.database().ref(`/users/${uid}`).once("value")
    .then(function(dataSnapshot) {
      const userDataAll = dataSnapshot.val()
      const userData = {}
      userData.id = userDataAll.id
      userData.username = userDataAll.username
      userData.first = userDataAll.first
      userData.last = userDataAll.last

      promises.push(userData)
      console.log(userData)
    })
    .catch((error) => {
      // Re-throwing the error as an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError('unknown', error.message, error);
    });
  })

  return Promise.all(promises);

}

return fetchUserObjects(uids)

Solution

  • fetchUserObjects is always returning an empty array. Nothing is ensuring that the async work started by once() is copmlete before you push values into the array. Also notice that you're not actually pushing promises into that array. You're pushing plain old JavaScript objects. You need to push actual promises into the array instead, and you need to do it without having to wait for other promises to resolve. Here's what it should look like instead:

    const fetchUserObjects = function(uids){
      let promises = []
      uids.forEach((uid) => {
        const promise = admin.database().ref(`/users/${uid}`).once("value")
        .then(function(dataSnapshot) {
          const userDataAll = dataSnapshot.val()
          const userData = {}
          userData.id = userDataAll.id
          userData.username = userDataAll.username
          userData.first = userDataAll.first
          userData.last = userDataAll.last
    
          console.log(userData)
          return userData
        })
        .catch((error) => {
          // Re-throwing the error as an HttpsError so that the client gets the error details.
          throw new functions.https.HttpsError('unknown', error.message, error);
        });
    
        promises.push(promise)
    
      })
    
      return Promise.all(promises);
    
    }
    

    Notice that a promise is immediately pushed into the promises array, and it will resolve with a userData object that was returned by the then callback.