Firebase查询:为什么在以下代码中的值查询之前调用了child_added? [英] Firebase query: Why is child_added called before the value query in the following code?

查看:57
本文介绍了Firebase查询:为什么在以下代码中的值查询之前调用了child_added?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a schema in Firebase that looks like this:

messages/
  $groupId/
    $messageId/
      message: 'Sample Message'
      createdBy: 'userID'
      createdAt: 1513337977055

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

Then I have the following queries executed consecutively in my code:

// 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()))

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

I'm wondering why child_added gets called twice here, the first one being similar to the value returned by the once('value') query.

以下是控制台显示的内容:

Here's what the console displays:

child_added { message: 'Hello', createdAt: 1513337977055, createdBy: 'userId' }
value { message: 'Hello', createdAt: 1513337977055, createdBy: 'userId' }
child_added { message: 'Another message', createdAt: 1513337977066, createdBy: 'userId2' }

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

Note that I'm not adding new entries to Firebase here. Just querying.

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

Here is a fiddle link demonstrating the issue: https://jsfiddle.net/dspLwvc3/2/

推荐答案

firebase here

你在那里发现了一个非常有趣的边缘案例。您所看到的行为是系统运行的预期。但我认为我们都同意它远非直观。 : - /

You've uncovered a quite interesting edge case there. The behavior you're seeing is expected from how the system operates. But I think we can all agree that it's far from intuitive. :-/

这实际上是一种竞争条件,加上Firebase对它将会发生什么,不会发生什么以及何时发生事件的保证。

It's essentially a race condition, combined with Firebase's guarantees about what it will and won't fire and when it fires events.

基本上会发生这样的事情:

Essentially what happens is this:

    Client                  Server
      |                       |
   (1)|  --once('value'---->  |
      |                       |
   (2)|  -on('child_added'->  |
      |                       |
      |           .           |
      |           .           |
      |           .           |
      |                       |
      |         value         |
   (3)|  <------------------- |
      |                       |
      |         child         |
   (4)|  <------------------- |
      |                       |

那里有4个关键时刻:


  1. 您为 / messages / message1附加一次('值')听众。客户端将请求发送到服务器并等待。

  2. 您附加(-child_added 最后一次知道密钥 / messages 的监听器。客户端将请求发送到服务器并等待。

  1. You attach a once('value') listener for /messages/message1. The client sends the request to the server and waits.
  2. You attach a on(-child_added listener for the last-known key of /messages. The client sends the request to the server and waits.

对第一个请求的回复是ba来自服务器的ck。在这个阶段有两个听众。 一次('value 监听器是清除的,因此它会触发并被删除。但此时 / messages / message1 也是 / messages 的最后一个已知密钥,因此客户端也会触发 child_added 监听器。

The response to the first request comes back from the server. At this stage there are two listeners. The once('value listener is clear, so it fires and is removed. But at this point /messages/message1 is also the last-known key of /messages, so the client fires that child_added listener too.

正如我所说,这不是很直观。但从系统的角度来看,这是正确的行为这意味着你不想要这种行为,你需要以不同的方式使用API​​。最简单的一个用你当前的代码将移动附加 child_added 听众进入 一次('值'回调:

As I said, it's not very intuitive. But from the system's perspective it is the correct behavior. That means that you don't want this behavior, you will need to use the API in a different way. The simplest one with your current code would be to move attaching the child_added listener into the once('value' callback:

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()))
  })

这是因为,当 child_added 侦听器, / messages / message1 快照已从客户端缓存中刷新。

This works because, by the time the child_added listener is attached, the /messages/message1 snapshot has already been flushed from the client's cache.

更新(2018-01-07):另一位开发人员遇到此行为并且很难维护子项的顺序。所以我写了一些关于这种行为(虽然意外)仍然保持孩子的正确顺序的更多信息。有关详细信息,请参阅我的答案: Firebase缓存废墟检索顺序孩子

Update (2018-01-07): another developer encountered this behavior and had a hard time maintaining the order of children. So I wrote up a bit more how this behavior (while unexpected) still maintains the correct order of the children. For more, see my answer here: Firebase caching ruins order of retrieved children

这篇关于Firebase查询:为什么在以下代码中的值查询之前调用了child_added?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆