在 Firebase 中使用事务时,为啥 onChildChanged() 在 onComplete() 之前触发?

Posted

技术标签:

【中文标题】在 Firebase 中使用事务时,为啥 onChildChanged() 在 onComplete() 之前触发?【英文标题】: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.
  
);

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

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

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

假设,如果房间已满……

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

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

有什么想法吗?

【问题讨论】:

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

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

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

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

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

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

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

【讨论】:

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

以上是关于在 Firebase 中使用事务时,为啥 onChildChanged() 在 onComplete() 之前触发?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在使用 kotlin 将数据添加到 firebase 实时数据库时会得到重复值?

React/Firebase 托管:为啥在页面刷新时要求用户登录?

为啥当 Enlist=False 时 Sqlserver 在事务中登记?

为啥在firebase onAuthStateChanged中完成活动时最近的应用程序中没有屏幕截图

当 XG(跨组)事务可以做同样的工作时,为啥要选择实体组事务?

当我使用此代码 firebase Swift 4 时,为啥我的 tableview 会出错