在 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
为了让我的客户占据其中一个插槽(one
到 three
),我使用以下代码 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中完成活动时最近的应用程序中没有屏幕截图