I have 2 "limit" queries on the same path. I first load a "limit(1)", and then later load a "limit(50)".
When I load the second query, the child_added events don't fire in-order. Instead, the last item in the list (the one returned by limit(1)) is fired first, and then all of the other items are fired in-order, like this:
**Limit 1:**
new Firebase(PATH).limit(1).on('child_added', ...)
Message 5
**Limit 2:**
new Firebase(PATH).limit(50).on('child_added', ...)
Message 5
Message 1
Message 2
Message 3
Message 4
I'm confused why "Message 5" is being called first in the second limit operation. Why is this happening, and how can I fix it?
I know this may seem strange, but this is actually the intended behavior.
In order to guarantee that local events can fire immediately without communicating with the server first, Firebase makes no guarantees that child_added events will always be called in sort order.
If this is confusing, think about it this way: If you had no internet connection at all, and you set up a limit(50), and then called push() on that reference, you would you expect an event to be fired immediately. When you reconnect to the server though, it may turn out that there were other items on the server before the one you pushed, which will then have their events triggered after the event for the one you added. In your example, the issue has to do with what data has been cached locally rather than something written by the local client, but the same principle applies.
For a more detailed example of why things need to work this way, imagine 2 clients, A and B:
Now, note that even though the message Client A added comes first in the list (since it has an earlier timestamp), the event is fired second.
So as you see, you can't always rely on the order of events to reflect the correct sort order of Firebase children.
But don't worry, you can still get the behavior you want! If you want the data to show up in sort order rather than in the order the events arrived on your client, you have a couple of options:
1) (The naive approach) Use a "value" event instead of child_added, and redraw the entire list of items every time it fires using forEach. Value events only ever fire for complete sets of data, so forEach will always enumerate all of the events in order. There's a couple of downsides to this approach though: (1) value events won't fire until initial state is loaded from the server, so it won't work if the app is started in "offline mode" until a network connection can be established. (2) It inefficiently redraws everything for every change.
2) (The better approach) Use the prevChildName argument in the callback to on(). In addition to the snapshot, the callback for on() is passed the name of the previous child in in the query when items are placed in sort order. This allows you to render the children in the correct order, even if the events are fired out of order. See: https://www.firebase.com/docs/javascript/firebase/on.html
Note that prevChildName only gives the previous child in the query, not in the whole Firebase location. So the child at the beginning of the query will have a prevChildName of null, even if there is a child on the server that comes before it.
Our leaderboard example shows one way to manipulate the DOM to ensure things are rendered in the proper order. See: https://www.firebase.com/tutorial/#example/leaderboard
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With