Firebase 如何取消订阅

Posted

技术标签:

【中文标题】Firebase 如何取消订阅【英文标题】:Firebase how to unsubscribe 【发布时间】:2017-12-06 09:40:44 【问题描述】:

我正在使用Ionic2Angularfire2 来访问Firebase Authentication

我访问以下rxjs/Observable

chats.ts

  this.firelist = this.dataService.findChats();
  this.subscription = this.firelist.subscribe(chatItems => 
     ...
  );

  ngDestroy() 
    this.subscription.unsubscribe();
  

dataService.ts

findChats(): Observable<any[]> 
        this.firebaseDataService.findChats().forEach(firebaseItems => 
           ...
           observer.next(mergedItems);
        );

firebaseDataService.ts

findChats(): Observable<any[]>  // populates the firelist
    return this.af.database.list('/chat/', 
        query: 
            orderByChild: 'negativtimestamp'
        
    ).map(items => 
        const filtered = items.filter(
            item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
        );
        return filtered;
    );

此外,在 Firebase 身份验证中,我有以下实时数据库规则:

  "rules": 
      ".read": "auth != null",
      ".write": "auth != null",
  

问题

这在用户登录时完美运行(即auth != null),但一旦用户注销,auth == null,我收到以下错误:

错误错误:未捕获(承诺):错误:permission_denied at /chat: 客户无权访问所需数据。错误: permission_denied at /chat:客户端没有访问权限 所需的数据。

问题

正如您在上面的代码中看到的,我尝试从 Firebase 身份验证服务unsubscribe,但一定是做错了。如果有人可以建议我应该如何unsubscribe 以停止我的应用程序查询 Firebase 数据库,我将不胜感激。

【问题讨论】:

你为什么不做this.firelist.unsubscribe() 您好 Skeptor,感谢您的反馈。 this.firelistrxjs/Observable&lt;any&gt;,api 没有unsubscribe 函数。但是,它确实返回了一个Subscription,它确实有一个,所以这就是我没有成功尝试的方法,如上所示。 reactivex.io/rxjs/class/es6/Observable.js~Observable.html 我从不退订,所以不确定语法。但在我看来,您需要查看每个 http 调用的 auth 对象状态,而不是取消订阅。我将提供一种不同的方式来编写相同的代码以及我在我的应用程序中的操作方式。 (我使用aws,但它们几乎相同) 谢谢您的帮助。 如果黑客登录并试图访问其他用户的消息会发生什么?这就是 Firebase 规则派上用场的地方。您只能允许访问与用户 uid 匹配的消息。您可以执行以下操作:` "chat": "$key": ".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2 ').val() === auth.uid || true", ".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2' ).val() === auth.uid || true" ,` 【参考方案1】:

这不是问题的确切答案,但我的论点是,如果我们这样做,我们将永远不会遇到这个问题。

P.S : 我从来没有使用过 firebase ,我使用 AWS 所以 firebase 代码可能不准确,我们讨论一下。

这是我的版本:

findChats(): Observable<any[]>  // populates the firelist
    return 
      this.authService.getCurrentUser()
        .flatMap(user => user === null ? 
          Observable.empty():        
          this.af.database.list('/chat/', 
             query: 
                orderByChild: 'negativtimestamp'
             
           )
          .map(items => 
             return items.filter(
               item => (
                  item.memberId1 === this.me.uid || 
                  item.memberId2 === this.me.uid
               )
             ) 
          )
     );

认证服务

public currentUser = new BehaviorSubject(null);

constructor()

    firebase.auth().onAuthStateChanged(function(user) 
        if (user) 
           // User is signed in.
           currentUser.next(user);
         else 
         // No user is signed in.
           currentUser.next(null);
        
    );

    

代码非常原始,但我希望它传达了这个想法。在这里,我正在观察用户身份验证状态并执行flatMap 来获取数据。如果用户注销,身份验证状态更改为 null,因此 UI 数据更改为空列表。即使用户没有登出,他仍然看不到数据。


更新

重构其他答案:

findChats()
   return Observable.merge(
       this.firebaseDataService
           .findChats()
           .flatMap(chats => Observable.from(chats)),
       this.localDataService
           .findChats()
           .flatMap(chats => Observable.from(chats))
   )
   .filter(chat => !!chat)
   .toArray()
   .map(chats => [
      ...chats.sort(
        (a, b) => 
           parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp))
      ]
   );  

除非您使用Subject 缓存响应,否则永远不要在服务内部使用subscribe

【讨论】:

【参考方案2】:

unsubscribe 工作正常。问题是我使用的以下函数由于某种原因导致它订阅两次:

findChats(): Observable<any[]> 
    return Observable.create((observer) => 
        this.chatSubscription2 = this.firebaseDataService.findChats().subscribe(firebaseItems => 
            this.localDataService.findChats().then((localItems: any[]) => 
                let mergedItems: any[] = [];
                if (localItems && localItems != null && firebaseItems && firebaseItems != null) 
                    for (let i: number = 0; i < localItems.length; i++) 
                        if (localItems[i] === null) 
                            localItems.splice(i, 1);
                        
                    
                    mergedItems = this.arrayUnique(firebaseItems.concat(localItems), true);
                 else if (firebaseItems && firebaseItems != null) 
                    mergedItems = firebaseItems;
                 else if (localItems && localItems != null) 
                    mergedItems = localItems;
                
                mergedItems.sort((a, b) => 
                    return parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp);
                );
                observer.next(mergedItems);
                this.checkChats(firebaseItems, localItems);
            );
        );
    );

由于某种原因,上面的第 3 行被调用了两次(即使该函数只被调用了一次)。这将创建第二个subscription,而第一个subscription 永远不会是unsubscribed

看来Observable 的创建是不正确的。我不确定创建Observable 的正确方法是什么。

【讨论】:

我已经发布了一个关于如何创建Observable 的问题,以便这里只有一个订阅:***.com/questions/44883886/… 你不应该使用 Observable.create 。使用 map 做一些转换后直接返回this.firebaseDataService 我不知道该怎么做,因为使用 Observable 的列表当前正在订阅它,即它需要动态更新。 我将使用重构方法更新我的答案

以上是关于Firebase 如何取消订阅的主要内容,如果未能解决你的问题,请参考以下文章

如何成功取消订阅用户通知(firebase,react)

订阅和取消订阅主题 - firebase

Firebase 云消息传递为已取消订阅的令牌返回 200 OK

离子生命周期取消/订阅 Firebase 数据库流(使用异步管道)

Firebase:如何停止收听快照

Firebase 事件侦听器的退避政策是啥?