Error using foreach and Promisse.all in node - node.js

I am stuck with this code:
await Promise.all(auxusers.forEach(async (element: any) => {
const userPopulated: any = new Object();
userPopulated.hours = await this.hoursRepository.find({ where: { userId : { like: element.id}}});
userPopulated.generalinfo = element;
usersArray.push(userPopulated);
}
));
return usersArray
It´s return me: Promise.all for each, error: Cannot read property 'Symbol(Symbol.iterator)' of undefined
Any idea? I am really don´t know what more can I trie
Thanks in advance.
Best,

The problem is that foreach does not return an array.
I would suggest to use map instead.
Check the code below:
const users = [1,2,3,4,5];
Promise.all(
users.map(async (element) => {
const userPopulated = {};
userPopulated.hours = Promise.resolve(element);
userPopulated.generalinfo = element;
return element;
})
).then((a) => console.log(a))

Related

Facing issue with scooping in node JS

This code is showing empty object ( {} )
// declared at top
let mainData = {};
let trainStations = {};
let routes = {};
let trainNo = {};
data["data"].forEach(async (element) => {
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
});
console.log(trainNo);
if i do this then i will give response with data
data["data"].forEach(async (element) => {
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
console.log(trainNo);
});
maybe there is some scooping issue please kindly help me to solve this problem :)
Please refer here and also check this.
As a short note, using await inside a forEach() loop will give unexpected results. This is because the forEach() does not wait until the promise to settled (either fulfilled or rejected).
A simple solution for this could be using either the traditional for loop or the for..of loop.
for(let element of data["data"]){
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
}
console.log(trainNo);
NOTE: Make sure to wrap the above for..of loop inside an async function because the await keyword is allowed inside a function only when the function is defined with async keyword.

'Unexpected token' when recursively calling async function in Nodejs

My app contains posts with nested comments in Firebase Firestore structured such that each post/comment with docID has a sub collection postComments. Thus, a given post/comment can have an infinite number of nested comments.
comments
- docID
postComments
- docID
- docID
- docID
- docID
postComments
- docID
- docID
I am currently writing a Firebase cloud function to recursively query all documents and sub collection documents of a given docID and return all of those documents in an array. My plan was to define the getChildComments async function which takes in a docID and returns all of the documents in that document's postComments sub collection. I would then recursively call getChildComments until I have built an array with all of the nested comments in a thread.
exports.loadWholeCommentThread = functions.https.onCall(async (data, context) => {
let comments = await getChildComments(data.rootID);
return comments;
});
async function getChildComments(docID) {
try {
const db = admin.firestore();
const commentsRef = db.collection('comments').doc(docID).collection('postComments');
var comments = [];
const commentsQuerySnap = await commentsRef.get();
commentsQuerySnap.forEach((comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
comments.concat(childComments);
}
});
return comments;
} catch (error) {
functions.logger.log(error);
throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
}
}
Unfortunately, when I try to deploy my code, I get the error Parsing error. Unexpected token getChildComments on the line where I recursively call getChildComments inside of getChildComments. Removing the await from this line fixes the build issue but then the recursive call doesn't finish.
How should I fix my issue? Or is there a better way to query all nested documents?
This is because you have used await outside of an async function (note that it is inside an arrow function!).
const comments = [];
const commentsQuerySnap = await commentsRef.get();
commentsQuerySnap.forEach((comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID); // the keyword "await" here is invalid
comments = comments.concat(childComments);
}
});
But you can't just add async to this arrow function, because then your code won't properly wait for the comments array to be filled.
To properly fix this, you need to use .map() on the commentsQuerySnap.docs array in addition to using Promise.all to wait for each comment (and its children) to be retrieved.
const comments = [];
const commentsQuerySnap = await commentsRef.get();
await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
let commentData = comment.data();
comments.push(commentData);
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
comments = comments.concat(childComments);
}
})
)
);
While that above block works, the comments array may be out of order to what you were expecting. If you must maintain order of the comments fetched so they are in the same order as the query, you should return the built comments array for each document and then flatten them after they all have been retrieved.
// removed const comments = []; here
const commentsQuerySnap = await commentsRef.get();
const arrayOfCommentThreads = await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
let commentData = comment.data();
const commentThread = [commentData];
if (commentData.commentCount > 0) {
let childComments = await getChildComments(commentData.commentID);
commentThread = commentThread.concat(childComments);
}
return commentThread;
})
)
);
const comments = arrayOfCommentThreads.flat();
Personally, I prefer the spread operator to using .concat like so:
const commentsQuerySnap = await commentsRef.get();
const arrayOfCommentThreads = await Promise.all(
commentsQuerySnap.docs.map(
async (comment) => {
const commentData = comment.data();
if (commentData.commentCount === 0) {
return [commentData];
}
const childComments = await getChildComments(commentData.commentID);
return [commentData, ...childComments];
})
)
);
const comments = arrayOfCommentThreads.flat();

TypeError: tagIds.join is not a function

I am getting this error in javascript.
TypeError: tagIds.join is not a function
I am trying to join many tags id together and delete them at once, so here I used tagsId to join the tags and separator them with commas. in the console, it is working well when I tested it. but in my code, it doesn't work. here is the code.
const deleteTags = async (postId, tagIds) => {
const joinedTags = tagIds.join(",");
joinedTags = await db.sequelize.query(
`delete from posttags where postId = ${postId} and tagId in (${joinedTags});`,
{ type: db.sequelize.QueryTypes.destroy }
);
console.log("joinedTags", joinedTags);
res.json({ joinedTagsResults });
};
this is the error as well.
const joinedTags = tagIds.join(",");
^
TypeError: tagIds.join is not a function
if I am doing something wrong please let me know.
thanks
const deleteTags = async (postId, tagIds) => {
let joinedTags = tagIds;
// if tagIds is array
if (Array.isArray(tagIds)) joinedTags = tagIds.join(',');
...
};
const postId = 5;
deleteTags(postId, [315, 312]);
deleteTags(postId, "315"); // Now, this also working

async function doesn't wait of inside await in nodejs

I am implementing function monthlyRevenue.
Simply, it will return total monthly revenue,and it takes arguments of station array which will make revenues, month and year.
Problem
Inside of this function I have getStationPortion which will fetch the revenue portion of user's.
So I would like to make it return object like this.
stationsPortion = {station1 : 30, station2 : 20}
In the monthlyRevenue
const stationPortions = await getStationPortions(stations)
console.log("portion map", stationPortions //it will be shown very beginning with empty
getStationPortions
const getStationPortions = async (stations) => {
let stationPortions = {}
stations.map(async (value) => {
const doc = await fdb.collection('Stations').doc(value).get()
if (!doc.exists) {
console.log("NO DOC")
} else {
stationPortions[value] = doc.data().salesPortion
console.log(stationPortions) //it will be shown at the last.
}
})
return stationPortions
}
I thought that async function should wait for the result, but it does not.
I am kind of confusing if my understanding is wrong.
Thank you
(by the way, fdb is firebase admin(firestore)
Working code
const getStationPortions = async (stations) => {
let stationPortions = {}
await Promise.all(stations.map(async (value) => {
const doc = await fdb.collection('Stations').doc(value).get()
if (!doc.exists) {
console.log("NO DOC")
} else {
stationPortions[value] = doc.data().salesPortion
console.log(stationPortions)
}
}))
return stationPortions
}
module.exports = router;

Get all documents in collection using Cloud Firestore

I read several documentation but I don't understand why I should use an extra layer(foreach) in my code when I read all of the data inside a collection using Firebase (Cloud Firestore).
Here is the original documentation:
https://firebase.google.com/docs/firestore/query-data/get-data#get_all_documents_in_a_collection
Here is my code:
async loadUsers(): Promise<User[]> {
const users = new Array<User>();
const snapshot = await this.firestore.collection('users').get();
snapshot.forEach((collection) => {
collection.docs.forEach(doc => {
users.push(doc.data() as User);
});
});
return users;
}
As I understand it should work like this:
async loadUsers(): Promise<User[]> {
const users = new Array<User>();
const snapshot = await this.firestore.collection('users').get();
snapshot.forEach(doc => {
users.push(doc.data() as User);
});
return users;
}
Error message:
"Property 'data' does not exist on type 'QuerySnapshot'."
.collection().get() does NOT return an array; it returns a QuerySnapshot, which has a property .docs, which is an array of QueryDocumentSnapshot, each of which has a property .data, which is the data read from the document.
Documentation
https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference
In new modular firebase firestore(version 9.+) it should be like this:
import { getFirestore, collection, query, getDocs } from 'firebase/firestore/lite'
async readAll() {
const firestore = getFirestore()
const collectionRef = collection(firestore, '/users')
let q = query(collectionRef, orderBy('createTimestamp', 'desc'))
const querySnapshot = await getDocs(q)
const items = []
querySnapshot.forEach(document => {
items.push(document.data())
})
return items
}
I could not find any parameter on querySnapshot directly that is something like .docs was and included whole array before. So it is kinda like onSnapshot is and was.
Based on #LeadDreamer answer, I could manage to simplify the code
async loadUsers(): Promise<User[]> {
const users = new Array<User>();
await this.firestore.collection('users').get().subscribe(querySnapshot => {
querySnapshot.docs.forEach(doc => {
users.push(doc.data() as User);
});
});
return users;
}
There seems to be no other way but to iterate.
const q = query(collection(db, "item"));
getDocs(q).then( response => {
const result = response.docs.map(doc=>({
id: doc.id,
...doc.data(),
}))
console.log(result);
}).catch(err=>console.log(err))

Resources