Bart
Bart

Reputation: 325

firestore async await foreach vs for

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

Answers (1)

rphlmr
rphlmr

Reputation: 918

Foreach expect synchronous callback ;)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill

(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

Related Questions