Reputation: 19444
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);
}
}
Upvotes: 0
Views: 211
Reputation: 98
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
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);
}
});
});
};
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();
Upvotes: 2