【问题标题】:Why is onChildChanged() firing before onComplete() when using transactions in Firebase?在 Firebase 中使用事务时,为什么 onChildChanged() 在 onComplete() 之前触发?
【发布时间】:2014-12-08 14:23:07
【问题描述】:

使用 Firebase for Android,我将以下数据结构用于具有有限数量槽的聊天室:

/rooms
  /<roomid, generated by push()>
    /users
      one: null
      two: null
      three: null

为了让我的客户占据其中一个插槽(onethree),我使用以下代码 as provided here (JavaScript version)

var userid = "myuserid";
var ref = new Firebase("<my-firebase>.firebaseio.com/rooms/<roomid>/users");
ref.transaction(function(users) {
  if (!users.one) {
    // Claim slot 1
    users.one = userid;
    return users;
  } else if (!users.two) {
    // Claim slot 2
    users.two = userid;
    return users;
  } else if (!users.three) {
    // Claim slot 3
    users.three = userid;
    return users;
  }
  // Room is full, abort the transaction.
  return;
}, function(err, committed, snapshot) {
  if (committed && !err) {
    // Joined room successfully.
  } else {
    // Could not join room because it was full.
  }
});

经过一些测试,我发现这不起作用。我对以下两个解释它为什么不起作用的假设是否正确?

  1. runTransaction(...) (Android) 或transaction(...) (JavaScript) 中,您必须将可选参数fireLocalEvents (Android) 或applyLocally (JavaScript) 设置为false,否则您将收到来自交易虽然可能不成功。
  2. 即使将fireLocalEvents (Android) 或applyLocally (JavaScript) 设置为false,该引用上onChildChanged(...) 的事件也会触发之前您的事务在@987654335 中成功返回@。这是预期的行为吗?

对于这种特殊情况,这意味着我将收到聊天室已满的通知(ChildEventListener.onChildChanged(...)我知道 我自己我是获得最后一个位置的人(Transaction.Handler.onComplete(...))。

假设,如果房间已满……

  • 我在那里找到了一个插槽,执行了一些代码并启动了一些新的东西。
  • 我没有获得任何插槽,我只想从列表中隐藏房间(因为它不再可用)。

如果我想这样做,我必须将它的完整逻辑移至ChildEventListener.onChildChanged(...),因为那是我将首先收到事件的地方,对吗?否则,在我知道我是最后一个空位之前,房间就会被隐藏起来。

有什么想法吗?

【问题讨论】:

  • 看起来很直观,onchildchanged 会在 oncomplete 之前触发。
  • @danny117 为什么?在服务器实际告诉您导致更改的事务成功完成之前,服务器会通知您有关数据更改的信息?如果我在事务处理程序中打开本地事件,我会同意。但事实上,并非如此。这就是 fireLocalEvents 标志的用途。
  • onComplete 好像完成了?
  • @danny117 所以是的,在事务完成之前数据不应该更新。看来你同意了。

标签: android firebase firebase-realtime-database


【解决方案1】:

这实际上是使 Firebase 成为出色应用并为您节省大量编码的组件之一。要了解这对您有利的原因,我们需要先回过头来谈谈延迟补偿和离线功能。

如果您曾经编写过提供实时功能的应用程序,那么您就会遇到连接错误和临时中断的意大利面条式混乱。一旦连接恢复,它会导致大量的 if/else/then 逻辑重试。

Firebase 将所有这些复杂性抽象化,以便您的应用在离线时与在线时一样工作。您仍然可以监听事件、设置/保存/更新数据,并像在线一样继续。当连接恢复时,将应用事件。

同样,当您在本地执行 set() 操作时,在本地应用更改之前等待服务器回复非常慢。同样,这会导致大量用于延迟补偿的 if/else 逻辑——跟踪您何时在本地进行更改并立即显示它们,这样用户就不会觉得应用程序没有响应。

Firebase 再次在内部处理这些复杂性。由于生产中 99.9% 的写入操作都会成功,因为错误通常会导致发送无效内容,因此我们只需通过触发正确的事件在本地应用更改。然后,如果服务器以这种极端情况进行回复,我们会通过应用另一个更正状态的事件来编辑更改。

总而言之,当您进行更改时,Firebase 数据最终是一致的。因此,您将立即看到本地活动,这是一件好事。如果在此过程中出现问题,您将看到第二个事件来更正数据,可能在几百毫秒后。

【讨论】:

  • 谢谢!我已经了解 Firebase 的概念,但问题是另一回事。正如您在我的两个问题或假设的编号列表中看到的那样,我已关闭交易的本地事件。至少这应该是参数的用途,对吧?问题是本地和删除事件都在事务完成之前触发。因此,即使没有本地事件,来自服务器的同步事件也会在我知道我的事务已成功提交之前到达。这显然并不总是你想要的。这种行为是有意的还是错误的?
  • 嗨,Marco,我没有完全理解这里的问题。即使它没有直接回答问题,我也会将其留作上下文。我认为我们看到的是您的事件在事务完成后触发,但在您的 onComplete 回调之前触发。我想知道我们如何验证这一点?
  • 嗯,很容易重现事务+子事件监听器的事情。当你这样做时,你会看到onChildChanged(...) 确实always 触发before onComplete(...) 回调。如果像您所说的那样,这可能会被改变,即如果事务在子事件被触发之前真的完成了。我的意思是,如果是这样,为什么回调的顺序应该反过来呢?或者有什么解决方法的想法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 2011-10-08
  • 2018-11-14
  • 2014-06-08
相关资源
最近更新 更多