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'sget()
returns a promise - collect
get
promises in an array withmap
, and run them withPromise.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