Home > OS >  How to loop through an array of Firebase Firestore documents and push values into array
How to loop through an array of Firebase Firestore documents and push values into array

Time:12-04

I am writing a Firebase Cloud Function in node.JS that reads user data from Firebase Firestore. I am unable to push the token values into the token array and return all tokens at the end of the function. My code is as follows:

function getTokens(subscribers) {
    return new Promise(async function (resolve, reject) {
        const tokenArray = [];
        subscribers.forEach(async (subscriber) => {
            await firestore
                .collection('users')
                .doc(subscriber)
                .get()
                .then((user) => {
                    console.log("Getting user data for user: ", user);
                    const tokens = user.data().tokens;
                    if (tokens) {
                        tokens.forEach((token) => {
                            console.log("Adding token to token array"); // data is available here but does not push into tokenArray
                            tokenArray.push(token);
                        });
                    }
                }).catch((error) => { console.error(error); reject(error); });
        });
        console.log("Token Array -- Final: ", tokenArray);
        resolve(tokenArray);
    });
};
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

You cannot use async-await in a forEach loop. Try mapping an array of promises and then using Promise.all() as shown below:

function getTokens(subscribers) {
  return new Promise(async function (resolve, reject) {
    
    const subscribersDocs = await Promise.all(
      subscribers.map((subscriber) => {
        return firestore.collection("users").doc(subscriber).get();
      })
    );


    const tokenArray = subscribersDocs.reduce((acc, curr) => {
      const tokens = curr.data().tokens;
      if (tokens) {
        acc = acc.concat(tokens);
      }
      return acc;
    }, []);

    console.log("Token Array -- Final: ", tokenArray);
    resolve(tokenArray);
  });
}

Also checkout: Using async/await with a forEach loop

CodePudding user response:

The OP code can be corrected and made more concise as follows:

async function getTokens(subscribers) {
  const getUser = subscriber => firestore.collection('users').doc(subscriber).get();
  const promises = subscribers.map(getUser);
  const users = await Promise.all(promises);
  return users.map(user => user.data().tokens).flat()
}

Some notes:

  • decorate the function as async, since it is
  • don't create any extra promise with Promise.new(), Firestore's get() returns a promise
  • collect get promises in an array with map, and run them with Promise.all()
  • mapping the resulting user's data.tokens produces an array of arrays. Flatten it and you're done.
  • a catch that only throws, is just like having no catch
  • Related