Firebase查询:为什么在以下代码中的value查询之前调用child_add?

jay*_*aye 4 javascript firebase firebase-realtime-database

我在Firebase中有一个架构,如下所示:

messages/
  $groupId/
    $messageId/
      message: 'Sample Message'
      createdBy: 'userID'
      createdAt: 1513337977055
Run Code Online (Sandbox Code Playgroud)

然后,我在代码中连续执行以下查询:

// Get a specific message
ref.child('messages/$groupId/$messageId')
  .once('value')
  .then(snap => console.log('value', snap.val()))

// Start new message listener
ref.child('messages/$groupId')
  .orderByKey()
  .limitToLast(1)
  .on('child_added', snap => console.log('child_added', snap.val()))
Run Code Online (Sandbox Code Playgroud)

我想知道为什么child_added在这里被两次调用,第一个类似于once('value')查询返回的值。

控制台显示的内容如下:

child_added { message: 'Hello', createdAt: 1513337977055, createdBy: 'userId' }
value { message: 'Hello', createdAt: 1513337977055, createdBy: 'userId' }
child_added { message: 'Another message', createdAt: 1513337977066, createdBy: 'userId2' }
Run Code Online (Sandbox Code Playgroud)

请注意,我不会在此处向Firebase添加新条目。只是查询。

编辑:这是一个演示问题的小提琴链接:https : //jsfiddle.net/dspLwvc3/2/

Fra*_*len 5

在这里火力

您在那里发现了一个非常有趣的边缘案例。从系统的运行情况来看,您所看到的行为是预期的。但是我认为我们都可以同意这远非直觉。:-/

本质上讲,这是一种竞赛条件,结合了Firebase对它将触发和不触发以及何时触发事件的保证。

本质上会发生以下情况:

    Client                  Server
      |                       |
   (1)|  --once('value'---->  |
      |                       |
   (2)|  -on('child_added'->  |
      |                       |
      |           .           |
      |           .           |
      |           .           |
      |                       |
      |         value         |
   (3)|  <------------------- |
      |                       |
      |         child         |
   (4)|  <------------------- |
      |                       |
Run Code Online (Sandbox Code Playgroud)

这里有4个关键时刻:

  1. 您为附加了一个once('value')侦听器/messages/message1。客户端将请求发送到服务器并等待。
  2. on(-child_added为的最后一个已知键附加了一个侦听器/messages。客户端将请求发送到服务器并等待。

  3. 对第一个请求的响应从服务器返回。在此阶段,有两个侦听器。该once('value监听器是明确的,所以它使用与被删除。但是,这/messages/message1也是的最后一个已知键/messages,因此客户端也将触发该child_added侦听器。

  4. 响应/messages/message3从服务器返回。仅剩一个听众,它要求听到最后一条消息,因此它将触发。请注意,如果您也有一个的监听器child_removed,那么这时就可以了/messages/message1

正如我所说,这不是很直观。但是从系统的角度来看,这是正确的行为。这意味着您不需要这种行为,您将需要以其他方式使用API​​。与您现有的代码最简单的一个是移动的连接child_added监听once('value'回调:

ref.child('messages/$groupId/$messageId')
  .once('value')
  .then(snap => {
    console.log('value', snap.val()))

  // Start new message listener
  ref.child('messages/$groupId')
    .orderByKey()
    .limitToLast(1)
    .on('child_added', snap => console.log('child_added', snap.val()))
  })
Run Code Online (Sandbox Code Playgroud)

之所以child_added可行,是因为在连接侦听器时,/messages/message1快照已经从客户端的缓存中清除了。

更新(2018-01-07):另一个开发人员遇到此行为,并且很难维护孩子的顺序。因此,我写了一些更多的内容,这种行为(尽管出乎意料)如何仍然保持孩子的正确顺序。有关更多信息,请在此处查看我的答案:Firebase缓存破坏了被回收儿童的顺序