1

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

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

为了让我的客户使用其中一个插槽(oneto three),我使用了此处提供的以下代码(JavaScript 版本)

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(...)onComplete(...)

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

假设,如果房间已满...

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

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

有任何想法吗?

4

1 回答 1

1

This is actually one of the components that makes Firebase an amazing app and saves you tons of coding. To understand why this is in your benefit, we need to back for a second and talk about latency compensation and offline functionality.

If you've ever written an app that provides real-time features, then you've run into the spaghetti mess of connection errors and temporary outages. It leads to tons of if/else/then logic to retry things once the connection is restored.

Firebase abstracts all of this complexity so that your app works the same offline as it works when online. You can still listen for events, set/save/update data, and continue on as if online. When the connection is restored, the events are applied.

Similarly, when you do a set() operation locally, it's very slow to wait for the server to reply before applying the changes locally. Again, this leads to lots of if/else logic for latency compensation--tracking when you make changes locally and showing them immediately so the user doesn't feel like the app is unresponsive.

Firebase again handles these complexities internally. Since 99.9% of your write ops in production are going to succeed, as bugs are generally responsible for sending invalid content, we just apply the change locally by firing the correct events. Then, if the server replies with that extreme edge case, we redact the change by applying another event that corrects the status.

So in summary, Firebase data is eventually consistent when you make a change. Thus, you're going to see the local event immediately, and this is a good thing. If something goes wrong in the process, you'll see a second event to correct the data, probably a few hundred milliseconds later.

于 2014-10-14T15:51:02.413 回答