0

I have a query to find users from firestore DB. Then I need to find which user is online or not. I used build presence(real-time database) for that.

In my logic, sometimes return an empty array. status is true on realTime DB. I think it will return without finish for a loop.

const users: Array<any> = [];

await asyncForEach(query.docs, async (loc: any) => {
  const data = loc.data();
  await rtDb.ref(data.uid).once('value', function (snap) {
    if (snap.val().status === true) {
      users.push(data);
    }
  }).catch((e) => {
    console.log(`${e}`)
  });
});

return users;


async function asyncForEach(array: Array<any>, callback: Function) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}

1 Answer 1

2

It's tough to say where the error may be. If you are getting an empty array, are you sure loc.data() is not throwing an error? Or there are other errors stopping the push from happening?

You could use a combination of promises and Promise.all. You can use a standardfor-of

Promises

You can turn the realTime DB data return into a promise. Promise docs

  const rtDbPromise = (loc: QueryDoc): Promise<UserData | null> => {
    const data = loc.data();
    return new Promise((resolve, reject) => {
      rtDb.ref(data.uid).once("value", (snap: Snap) => {
        try {
          if (snap.val().status) {
            resolve(data);
          } else {
            resolve(null);
          }
        } catch (e) {
          reject(e);
        }
      });
    });
  };

Promise all

Then you can add all your promises to an array and use Promise.all. Looping with a for-of

type UserData = { uid: string };
type Snap = { val: () => { status: boolean } };
type QueryDoc = { data: () => UserData };
type MockRtDb = {
  ref: (
    uid: string
  ) => {
    once: (type: "value", callback: (snap: Snap) => void) => Promise<void>;
  };
};

async function getOnlineUsers(): Promise<UserData[]> {
  const rtDb = {} as MockRtDb;

  // Create the async call here (Promise)
  const rtDbPromise = (loc: QueryDoc): Promise<UserData | null> => {
    const data = loc.data();
    return new Promise((resolve, reject) => {
      rtDb.ref(data.uid).once("value", (snap: Snap) => {
        try {
          if (snap.val().status) {
            resolve(data);
          } else {
            resolve(null);
          }
        } catch (e) {
          console.log(e);
          // This will throw an error. If any promises fail, Promise.all will throw as well.
          // Maybe return null if you don't want errors. Read the docs for more info
          reject(e);
        }
      });
    });
  };

  const dataArray: QueryDoc[] = [];
  const promise: Promise<UserData | null>[] = [];

  for (const data of dataArray) {
    promise.push(rtDbPromise(data));
  }

  const dataWithNulls: (UserData | null)[] = await Promise.all(promise);
  // Filter out the nulls
  return dataWithNulls.filter((item) => item !== null);
}

const onlineUsers = await getOnlineUsers();
Sign up to request clarification or add additional context in comments.

1 Comment

@BloodLoss Sure np. Hope it goes well

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.