Angular8-可以观察到不取消订阅ngOnDestroy

时间:2019-10-07 12:17:21

标签: angular rxjs observable

我有一个聊天应用程序,可以说我正在与Jane聊天。当我打开与Jane的聊天屏幕时,她向我发送了一条消息,我正确地收到了它。但是,如果我关闭该消息,然后再次打开它,Jane向我发送一条消息,则我收到了它的两个副本。当我关闭屏幕并再次打开它时,Jane发送一条消息,我得到了它的三个副本。

似乎在关闭屏幕时,没有适当取消订阅该可观察对象。当我检查后端时,一次仅发送一条消息,从不发送多条消息。

chat.component.ts

export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
  private _messages: Subscription;
  private message = ''; //for the [(ngModel)]


  public ngOnInit(): void {
    // below I am subscribing to the observable
    this._messages = this.chatService.getMessages$().subscribe((message: Message[]): void => {
      console.lot('msg received', message);
      this.messages$ = message;
    });
  }

  public async sendMessage(): Promise<void> {
    const message = await this._bundleMessage(); // formats the message
    this.chatService.sendMessage(message);
    this.message = '';
  }

  public ngOnDestroy(): void {
    this._messages.unsubscribe();
  }
}

chat.service.ts

  private messages: Message[] = new Array<Message>();
  private readonly bsMessages$: BehaviorSubject<Message[]> = new BehaviorSubject<Message[]>(this.messages);

  public getMessages$(): Observable<Message[]> {
    return this.bsMessages$.asObservable();
  }

  public async setAsActiveConversation(user: UserData): Promise<void>{
    this._incomingMessages = this.receiveMessages().subscribe();
    const { id } = user;
    this.activeConversation = await this.conversationStorage.get(id);
    console.log('after setting the active conversation', this.activeConversation);
    if (this.activeConversation === null) {
      await this._newConversation(user);
      await this.conversationStorage.set(id, this.activeConversation);
      this.messages = this.activeConversation.messages;
      this.bsMessages$.next(this.messages);
    } else {
      await this.activeConversation.messages.forEach((msg: Message): void => {
        if (msg.image) {
          msg.image = this.webView.convertFileSrc(msg.image);
        } else {
          msg.image = '';
        }
        // console.log('after the if statement', this.activeConversation);
        this.messages = this.activeConversation.messages;
        this.bsMessages$.next(this.messages);
      });
    }
  }


  public async sendMessage(msg: Message): Promise<void> {
    this.socket.emit('message', msg); //emits to server
    this.activeConversation.messages.push(msg); //adds message to array
    this.conversationStorage.set(this.activeConversation.id, this.activeConversation); //saves message locally
    this.bsMessages$.next(this.messages); //signals to update
  }

如果您需要更多信息,请告诉我,但我认为可以归结为这一点

编辑:添加了更多代码来帮助澄清一些注释。另外,当我记录数组见上文的结果时,我可以看到有重复项被添加到数组中。不只是视觉上当它发送2条以上的消息时,我看到数组长度增加了2条以上

2 个答案:

答案 0 :(得分:1)

错误在这里:

  public async setAsActiveConversation(user: UserData): Promise<void>{
    this._incomingMessages = this.receiveMessages().subscribe(); //<--- here
    const { id } = user;

我并没有取消订阅,这导致了内存泄漏!

chat.component.ts

我添加了


  public ngOnDestroy(): void {
    this.chatService.setLastMessage();
    this._messages.unsubscribe();
    this.chatService._incomingMessages.unsubscribe(); // <---
  }

答案 1 :(得分:0)

尽管您可以将.subscribe()的返回值存储在局部变量中并最终自己调用.unsubscribe(),但这通常很麻烦且容易出错。相反,请考虑以下方法:

自定义管道:

// RxJs pipeable operator for subscribing until component fires onDestroy
export function takeUntilComponentDestroyed(component: OnDestroy): MonoTypeOperatorFunction<any> {
  const componentDestroyed = (comp: OnDestroy) => {
    const oldNgOnDestroy = comp.ngOnDestroy;
    const destroyed$ = new ReplaySubject<void>(1);
    comp.ngOnDestroy = () => {
      oldNgOnDestroy.apply(comp);
      destroyed$.next(undefined);
      destroyed$.complete();
    };
    return destroyed$;
  };

  return pipe(
    takeUntil(componentDestroyed(component))
  );
}

这为RxJ定义了一个自定义pipe,这将允许我们take直到销毁指定的组件。该组件必须必须像这样实现OnDestroy

类定义:

export class SomeComponent implements OnInit, OnDestroy { 
    ... 
    // OnDestroy interface contract requires:
    ngOnDestroy() { }
    ...
}

现在您已经设置了要实施OnDestroy的组件,则可以使用我们新的自定义管道自动take,直到其被破坏为止,如下所示:

observable$.pipe(
    takeUntilComponentDestroyed(this)
).subscribe(value => {
    console.log("Value emits until component destroyed", value)
});

现在,您可以在想收听的任何地方使用该管道,而不必担心取消订阅!