Reputation: 325
I am using firestore for a while now, I want to implement a call to get data from a subcollection.
I had to create a asynchronous call and foreach() method did not await the call and continued, but it worked with the for() method. Can someone please explain to me why for() waits and foreach() doesn't?
Foreach() - doesn't work
export const FirebaseSearchUsers = async (search: string) => {
const end = search.replace(/.$/, c => String.fromCharCode(c.charCodeAt(0) + 1));
let users: UserDocument[] = [];
await firestore()
.collection(FirebaseConstraints.UserCollection)
.where('username', '>=', search)
.where('username', '<', end)
.limit(10)
.get()
.then(async x => {
x.forEach(async y => {
let userDocument: UserDocument = y.data() as UserDocument;
userDocument.houseInvites = [];
await y.ref
.collection(FirebaseConstraints.HouseInvitesCollection)
.get()
.then(x => x.forEach(x => userDocument.houseInvites.push(x.data() as HouseInviteDocument)));
users.push(userDocument);
});
});
return users;
};
For() -works
export const FirebaseSearchUsers = async (search: string) => {
const end = search.replace(/.$/, c => String.fromCharCode(c.charCodeAt(0) + 1));
let users: UserDocument[] = [];
await firestore()
.collection(FirebaseConstraints.UserCollection)
.where('username', '>=', search)
.where('username', '<', end)
.limit(10)
.get()
.then(async x => {
for (let y of x.docs) {
let userDocument: UserDocument = y.data() as UserDocument;
userDocument.houseInvites = [];
await y.ref
.collection(FirebaseConstraints.HouseInvitesCollection)
.get()
.then(x => x.forEach(x => userDocument.houseInvites.push(x.data() as HouseInviteDocument)));
users.push(userDocument);
}
});
return users;
};
Upvotes: 0
Views: 513
Reputation: 918
Foreach expect synchronous callback ;)
(2nd yellow memo)
Your for version is good but it's blocking. (you can parallelize your seconds requests to get your results faster)
EDIT
To parallelize, maybe this should work ;)
export const FirebaseSearchUsers = async (search: string) => {
const end = search.replace(/.$/, c => String.fromCharCode(c.charCodeAt(0) + 1));
// wrap this in try catch to handle errors ;)
const usersQuerySnapshot = await firestore()
.collection(FirebaseConstraints.UserCollection)
.where('username', '>=', search)
.where('username', '<', end)
.limit(10)
.get();
const futureUsers = usersQuerySnapshot.docs.map(async userSnapshot => {
const userDocument: UserDocument = userSnapshot.data() as UserDocument;
const houseInvitesSnapshots = await userSnapshot.ref
.collection(FirebaseConstraints.HouseInvitesCollection)
.get();
const houseInvites = houseInvitesSnapshots.docs.map(snapshot => snapshot.data() as HouseInviteDocument);
// or you can use the builtin method forEach of houseInvitesSnapshots and declare a "let" houseInvites array
// let houseInvites = [];
// houseInvitesSnapshots.forEach(snapshot => houseInvites.push(snapshot.data() as HouseInviteDocument));
// these 2 forms should works same (I did not test ;)
// Then, return a copy of your user document and fill its houseInvites property
return { ...userDocument, houseInvites};
})
const users = await Promise.all(futureUsers);
return users;
};
Upvotes: 4