我在同一条路径上有2个“限制”查询。我首先加载“limit(1)”,然后加载“limit(50)”。
当我加载第二个查询时,child_added事件不会按顺序触发。相反,列表中的最后一项(由limit(1)返回的项)首先被触发,然后所有其他项按顺序触发,如下所示:
**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
我很困惑为什么在第二个限制操作中首先调用“消息5”。为什么会发生这种情况,我该如何解决?
答案 0 :(得分:9)
我知道这可能看起来很奇怪,但实际上这是预期的行为。
为了保证本地事件无需先与服务器通信即可立即触发,Firebase不保证始终按排序顺序调用child_added事件。
如果这让人感到困惑,可以这样考虑一下:如果你根本没有互联网连接,并设置了限制(50),然后在该引用上调用了push(),你会期望一个事件发生吗?立刻被解雇。当您重新连接到服务器时,可能会发现在您推送的服务器之前服务器上还有其他项目,然后在您添加的事件发生后将触发事件。在您的示例中,问题与本地缓存的数据有关,而不是本地客户端编写的内容,但同样的原则也适用。
有关事情需要以这种方式工作的更详细示例,请设想2个客户端A和B:
现在,请注意,即使客户端A添加的消息首先出现在列表中(因为它具有较早的时间戳),该事件将被触发第二个。
如您所见,您不能总是依赖事件的顺序来反映Firebase子项的正确排序顺序。
但别担心,你仍然可以得到你想要的行为!如果您希望数据按排序顺序显示,而不是按照事件到达客户端的顺序显示,则有几个选项:
1)(天真的方法)使用“value”事件而不是child_added,并在每次使用forEach触发时重绘整个项目列表。值事件只会触发整个数据集,因此forEach将始终按顺序枚举所有事件。这种方法有几个缺点:(1)在从服务器加载初始状态之前,值事件不会触发,因此如果应用程序以“离线模式”启动,它将无法工作,直到网络连接可以成立。 (2)每次改变都无法重新绘制所有内容。
2)(更好的方法)在on()的回调中使用prevChildName参数。除了快照之外,当按排序顺序放置项目时,on()的回调将传递查询中前一个子项的名称。这允许您以正确的顺序呈现子项,即使事件是无序触发的。请参阅:https://www.firebase.com/docs/javascript/firebase/on.html
请注意,prevChildName仅提供查询中的上一个子项,而不是整个Firebase位置。因此,查询开头的子节点的prevChildName将为null,即使服务器上有子节点也是如此。
我们的排行榜示例显示了一种操作DOM的方法,以确保以正确的顺序呈现内容。看到: https://www.firebase.com/tutorial/#example/leaderboard