Reputation: 1862
BACKGROUND
Hello guys, I am performing a query in Firebase that retrieves a list of messages:
ref.child("messages").on("child_added", function(snapshot) {
console.log(snapshot.val());
});
The above query works fine. It displays the messages in order.
A message contains the following properties/fields:
<message-id>: {
user-id: <user-id>,
message: "This is a sample message",
time: 1446534920014
}
PROBLEM
When displaying a message, I also need to display the user's name. So I need to add another query that retrieves the user's name using the user-id
field:
ref.child("messages").on("child_added", function(snapshot) {
var message = snapshot.val();
// get the user's name
ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
// logs the results in the wrong order
console.log(message, userSnapshot.val().name);
}
});
The problem was the inner query returns the list of messages in the wrong order. Why? And what should be the correct way in order to display the messages in the correct order?
EDIT
Ok here you go. Below is the exported JSON:
{
"messages" : {
"-K2FyInlsZqLdHou1QnA" : {
"message" : "Message 1",
"user-id" : "-K2Fxr1gdxynukjDzcIq"
},
"-K2FyOlw13MU9KHB5NQh" : {
"message" : "Message 2",
"user-id" : "-K2Fxr1gdxynukjDzcIq"
},
"-K2Fz2GxPgqGf8uDfK0d" : {
"message" : "Message 3",
"user-id" : "-K2Fy3uyw-RNcePo_Pn-"
}
},
"users" : {
"-K2Fxr1gdxynukjDzcIq" : {
"name" : "John Joe"
},
"-K2Fy3uyw-RNcePo_Pn-" : {
"name" : "Alfred"
}
}
}
My goal is just to display the messages (in order) along with the user's name. If I do this:
ref.child("messages").on("child_added", function(snapshot) {
console.log(snapshot.val());
});
It displays the messages in order, i.e. Message 1, Message 2, Message 3. But I still don't have the user's name so I need to look into the users list to retrieve the user's name before displaying the message:
ref.child("messages").on("child_added", function(snapshot) {
ref.child("users").child(snapshot.val()["user-id"]).once("value", function(userSnapshot) {
console.log(snapshot.val(), userSnapshot.val().name);
});
});
That returns a list of the messages including the user's name, but the problem is it's in the wrong order: Message 2, Message 1, Message 3
Upvotes: 2
Views: 1456
Reputation: 600130
The messages are retrieved in the order you'd expect. You can easily verify this by adding some additional logging:
ref.child("messages").on("child_added", function(snapshot) {
var message = snapshot.val();
console.log(message.message);
ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
console.log(message.message+': '+userSnapshot.val().name);
});
});
The output from this on my recent runs has been:
"Message 1"
"Message 2"
"Message 3"
"Message 2: John Joe"
"Message 1: John Joe"
"Message 3: Alfred"
What you see here in the first three lines is that the messages are in order. But you then start retrieving the user for each of the messages; and those users don't necessarily come back in the order you fired/expected them.
This a very common problem when using AJAX on the web: as soon as network traffic is involved, the order of your code is not necessarily the order in which things happen.
The solution is always the same: don't depend on your code executing in a specific order. For example: you most likely want to display the message in a list in the DOM somewhere.
var ul = document.getElementById('messages');
ref.child("messages").on("child_added", function(snapshot) {
var message = snapshot.val();
var li = document.createElement('li');
li.id = 'msg_'+snapshot.key();
li.innerText = message.message;
ul.appendChild(li);
ref.child("users").child(message["user-id"]).once("value", function(userSnapshot) {
li.innerText = userSnapshot.val().name + ': '+ li.innerText;
});
});
Side note: you might want to consider adding a cache of user names. You'll now be calling out to the database for each message, for each user. This is an O(n^2) operation, so does not scale very well with the number of users.
Repro: http://jsbin.com/zofasi/edit?js,console
Upvotes: 3