Is this a limitation to Firebase or am I doing this all wrong? Everything works until I add the db.collection('users').doc(friendId).get()...
in the middle of the code. Thanks in advance.
const db = admin.firestore();
const friendRef = db.collection('users').doc(id).collection('friends');
friendsList = [];
friendRef.get().then((onSnapshot) => {
if (!onSnapshot.empty) {
onSnapshot.forEach((friend) => {
const friendId = String(friend.data().person_id);
db.collection('users').doc(friendId).get().then((result) => {
const firstName = String(result.data().name.first);
const lastName = String(result.data().name.last);
})
const data = {
personId: friendId,
firstName: firstName,
lastName: lastName,
}
friendsList.push(data);
})
res.send(friendsList);
} else {
res.send({
'message': 'no friends'
});
}
}).catch((e) => {
res.send({
'error': e
});
})
Data is loaded from Firestore asynchronously. This means that by the time you're sending the response back to the client, the data hasn't loaded from Firestore yet.
The easiest way to see this is with some well placed logging statements:
When you run just this code it'll print:
That is probably not the order you expected the logs to be in. The reason is that the data may take some time to come back from Firestore. So instead of blocking the thread, it continues running the thread and then calls your callback function when the data is available. And that unfortunately means that your
res.send(friendsList)
ends up sending an empty list back to the client, since the data hasn't loaded yet.The solution to this is to use a bunch of nested callbacks, to use
Promise.all
(), or ES6's newasync
/await
keyword. With promises the code looks like this:So we first build a list of all friend read operations, then once all of those are done, we build the response and send it back.