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);
});
};
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:
async, since it includes an awaitPromise.new(), Firestore's get() returns a promiseget promises in an array with map, and run them with Promise.all()data.tokens produces an array of arrays. Flatten it and you're done.catch that only throws, is just like having no catchYou 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