嵌套的可观察订阅问题,无法取消订阅

时间:2019-07-04 21:40:40

标签: angular typescript firebase rxjs google-cloud-firestore

我在我的应用中将Firebase身份验证与Firestore(https://github.com/angular/angularfire2)一起使用。根据我当前的Firestore规则,我确保在退出之前取消订阅从Firestore检索到的所有可观察到的内容。但是,我收到“ FirebaseError:缺少或权限不足”。错误,我可以追溯到我在订阅中订阅的嵌套订阅,如下面的代码所示。

在ngOnDestory内部,我同时取消了两个订阅,但是仍然收到上一个错误。

this.proposalSubscription = this._posts.retrieveProposals(user.uid)
  .subscribe(proposals => {
      this.proposals = proposals;
      this.proposals.forEach(proposal => {
        this.chatSubscription = this._chat.retrieveChat(proposal.id).subscribe(chat => {
          if (chat) {
            this.chats.set(proposal.id, chat)
          }
        });
      })
    });

我几乎可以确定问题出在以下一行:this.chatSubscription = this._chat.retrieveChat(proposal.id).subscribe(chat => ...即使我在退出前同时取消了proposalSubscription和chatSubscription,也仍然会收到错误消息。关于如何解决此问题的任何想法?另外,我对rxjs和运算符没有太多经验。有没有可以用来避免这种嵌套订阅的运算符?

谢谢。

2 个答案:

答案 0 :(得分:2)

您可以这样设置订阅:

this.proposalSubscription = this._posts.retrieveProposals(user.uid)
                                    .pipe(
                                      switchMap(proposals => {

                                        //if you need to save it in class variable you can save it like this
                                        this.proposals = proposals;

                                        const obs$ = proposals.map(p => {
                                          return this._chat.retrieveChat(p.id)
                                                     .pipe(
                                                       map(chat => {
                                                         this.chats.set(p,id, chat);
                                                       })
                                                     )
                                        });

                                        return forkJoin(obs$);
                                      })
                                    )
                                    .subscribe();

您可以根据需要组成运营商链,并且只有一个订阅。

ngOnDestory中取消订阅,如下所示:

ngOnDestroy() {

 if(this.proposalSubscription) {
   this.proposalSubscription.unsubscribe()
 }
}

如果希望评估unsubscribe仅可观察一次,则可以避免使用ngOnDestroySubscription中显式take(1)并维护this._posts.retrieveProposals(user.uid)实例像这样:

this._posts.retrieveProposals(user.uid)
                                    .pipe(
                                      take(1),
                                      switchMap(proposals => {

                                        //if you need to save it in class variable you can save it like this
                                        this.proposals = proposals;

                                        const obs$ = proposals.map(p => {
                                          return this._chat.retrieveChat(p.id)
                                                     .pipe(
                                                       map(chat => {
                                                         this.chats.set(p,id, chat);
                                                       })
                                                     )
                                        });

                                        return forkJoin(obs$);
                                      }),                                          
                                    )
                                    .subscribe();

答案 1 :(得分:1)

嵌套订阅并不是处理相关可观察变量的最佳方法,我们通常使用switchMap。在同一组件中管理多个订阅的最佳方法是使用takeUntil,因为您可以将值发送到单个主题中,并一次性取消所有订阅。我们可以将提议映射到一个可观察对象数组,并使用CombineLatest返回这些可观察对象的结果数组。

finalise = new Subject();

this._posts.retrieveProposals(user.uid)
  .pipe(
    switchMap(proposals => combineLatest(proposals.map(proposal => this._chat.retrieveChat(proposal.id)))),
    takeUntil(finalise)
  ).subscribe(chats => {
    const chat = proposals.find(p => p);
    this.chats.set(proposal.id, chat)
  });

和ngOnDestroy

this.finalise.next();

所有观看的订阅都将在takeUntil中结束。