13

我一直在广泛使用 Firebase,但仍然只面临一个真正的问题:根据我的经验,onDisconnect 并不是 100% 可靠的。

如果您在没有先关闭窗口的情况下关闭了计算机,或者关闭了浏览器,那么您有时会让“垃圾收集器”执行 onDisconnect,有时则不会。

我的问题如下:我只是暂时不使用/.connected,我基本上使用一个简单的

userRef.set('status', 1);
userRef.onDisconnect().update({ 'status' : 0 });

这种方法有什么问题吗?我们是否同意更新参数在执行该行时而不是在窗口卸载之前传递给服务器?

注意:我碰巧尝试保持多窗口状态,如果另一个窗口关闭,则使用以下方法将状态保持为 1:

userRef.child('status').on('value', function(snap) {
  if (snap.val() != 1) {
    userRef.set('status', 1);
  }
});

我不知道这有什么关系,但是...

我的解决方案:事实上,我刚刚错过了您了解到 onDisconnect 仅触发一次的部分。要获得持久的 onDisconnect,您需要实现基本的持久性。

Helpers.onConnected = function(callback) {
    var connectedRef = lm.newFirebase('.info/connected');
    var fn =  connectedRef.on('value', function(snap) {
      if (snap.val() === true) {
          if (callback) callback();
      }
    });
    var returned = {};
    returned.cancel = function() {
        connectedRef.off('value', fn);
    };
    return returned;
};       

简单用例:

        this._onConnected = lm.helpers.onConnected(function() {
            this.firebase.onDisconnect().update({ 'tu': 0 });
        }.bind(this));

然后取消:

        if (this._onConnected) this._onConnected.cancel();
        this.firebase.onDisconnect().cancel();
4

3 回答 3

6

在调用 set() 操作之前,您应该始终调用 onDisconnect() 操作。这样,如果两者之间的连接丢失,您最终不会得到僵尸数据。

另请注意,在网络连接未完全终止的情况下,您可能必须等待 TCP 超时,然后我们才能检测到用户已离开并触发断开连接清理。将进行清理,但可能需要几分钟。

于 2013-06-12T17:22:40.043 回答
2

经过大量挖掘,我发现这个问题实际上发生在我的案例中,我认为对于大多数其他人来说,他们也会来到这个页面寻求解决方案。

所以问题是当 1.入队和2.被应用accessToken时,firebase 会检查两次。当看不到选项卡时,火箱不会主动刷新令牌。如果页面处于非活动状态的时间超过了并且在没有关注该选项卡的情况下关闭了该选项卡,firebase 将不会因为已过期而允许。onDisconnectonDsiconnectaccessTokenonDisconnectaccessToken

对此的解决方案:

  1. accessToken您可以通过设置某种间隔来获得新的,如下所示:

    let intervalId;
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'visible') {
        if (intervalId) {
          window.clearTimeout(intervalId);
          intervalId = undefined;
        }
      } else {
        firebaseApp.auth().currentUser.getIdToken(true);
    
        intervalId = setInterval(() => {
          firebaseApp.auth().currentUser.getIdToken(true);
        }, intervalDuration);
      }
    });
    
  2. firebase.database().goOffline()或者,您可以在选项卡可见性从“可见”更改时手动断开数据库。

于 2019-07-08T11:12:35.830 回答
1

扩展评论:

This is why we provide the /.info/connected endpoint. 
We recommend re-establishing the disconnect handler every time the connection comes back online

我按照上面的方法修复了它:

const userRef = Firebase.database().ref('/users/<user-id>')

Firebase.database()
  .ref('.info/connected')
  .on('value', async snap => {
    if (snap.val() === true) {
      await userRef.onDisconnect().remove();
      await userRef.update({ status : 'online' })
    } else {
      await this.userRef.remove()
    }
});

作为参考,我之前的代码是:

const userRef = Firebase.database().ref('/users/<user-id>')
userRef.onDisconnect().remove();
await userRef.update({ status : 'online' })

这样做的问题是,当您离线并多次重新在线时,onDisconnect 可能无法正常工作

于 2020-10-28T03:35:56.727 回答