簡體   English   中英

Angular observables - 如果沒有訂閱,我需要取消訂閱嗎?

[英]Angular observables - Do I need unsubscribe if no subscription?

我正在使用最新的 angular 8 並且對 observables 的概念不熟悉。 我有一個問題,如果我直接調用一個可觀察對象而不將其應用於訂閱變量,我還需要取消訂閱嗎? 以下是我想知道是否需要退訂的場景? 提前謝謝了

場景 1 - 從組件調用 httpService:

Service - httpService

     getContactsHttp(){
         let headers: any = new HttpHeaders(this.authService.getHeadersClient());
         return this.httpClient.get('/contacts', {headers: headers})
          .pipe(timeout(this.authService.getTimeoutLimit('normal')));
        }

Component - Calling getContactsHttp and sorting response

getContacts() {
 this.httpService.getContactsHttp().subscribe((data:any[])=>{
  this.records = this.sortData(data)
 })
}

場景 2 - 在組件中訂閱的 observable

contacts$: new Subject<any[]>;

ngOnInit() {
  this.getContacts();
  this.contacts$.subscribe((data:any[])=>{
    this.records = this.sortData(data);
  })
}

getContacts() {
    this.httpService.getContactsHttp().subscribe((data:ContactSearch[])=>{      
      this.contacts$.next(data);
    })
  }

服務 - httpService

     getContactsHttp(){
         let headers: any = new HttpHeaders(this.authService.getHeadersClient());
         return this.httpClient.get('/contacts', {headers: headers})
          .pipe(timeout(this.authService.getTimeoutLimit('normal')));
        }

簡短的回答,是的,您仍然取消訂閱組件中的 observables 以 避免訂閱泄漏 我首選的方法之一是使用takeUntil()運算符。

這就是您可以在組件中使用它的方式。

private unsubscribe: Subject<void> = new Subject();

ngOnDestroy() {
  this.unsubscribe.next();
  this.unsubscribe.complete();
}

getContacts() {
  this.httpService.getContactsHttp()
    .pipe(
      takeUntil(this.unsubscribe),
    ).subscribe((data:ContactSearch[])=>{      
      this.contacts$.next(data);
    });
 }

正如Brian Love所解釋的,

  • 首先,我們導入 takeUntil() 運算符以及 Subject class。
  • 接下來,我們定義一個名為 unsubscribe 的私有實例屬性,它是一個 Subject。
  • 我們還創建了一個 Subject 的新實例,將泛型類型定義為 void。 在調用 subscribe() 之前,我們在 pipe() 方法中使用 takeUntil() 運算符,提供 unsubscribe observable。
  • 在 ngOnDestroy() 生命周期方法中,我們發出一個 next() 通知,然后完成() unsubscribe observable。 訂閱現已完成,當在組件的生命周期中調用 ngOnDestroy() 方法時,我們立即取消訂閱。

1)一般情況下,直接撥打http電話時不需要退訂。 即使組件被銷毀,銷毀后訂閱完成的開銷也是微不足道的。 如果快速切換組件,您需要在此處取消訂閱。 取消訂閱也會取消 http 請求,因此如果需要,請取消訂閱。

取消訂閱不會造成任何傷害。 如果您不確定,請始終退訂。

2)當您訂閱一個在您的組件被銷毀時未完成的可觀察對象時,您確實需要取消訂閱。 否則,這將導致 memory(和性能)泄漏。 因為 observable 本身持有對訂閱的引用,並且訂閱持有對組件的引用,所以組件永遠不會從 memory 中清除,並且訂閱中描述的操作將一直運行直到 observable 完成,在您的情況下永遠不會. 這將發生在您的組件的每個實例上。

解決方案

我將分享兩個關於簡化退訂負擔的流行選項。 擴展 @amanagg1204 答案,您可以創建一個基礎組件,您可以從中擴展所有未來的組件。 您可以在其中包含自定義運算符。 這樣做有一個缺點 - 如果您需要在組件中使用ngOnDestroy ,則始終必須調用super.ngOnDestroy()

import { OnDestroy } from "@angular/core";
import { Subject, MonotypeOperatorFunction } from "rxjs";
import { takeUntil } from "rxjs/operators";

export abstract class UnsubscribeComponent implements OnDestroy {
  protected destroyed$: Subject<void> = new Subject();

  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }

  takeUntilDestroyed<T>(): MonoTypeOperatorFunction<T> {
      return takeUntil(this.destroyed$);
  }
}

export class Component extends UnsubscribeComponent {
   ngOnInit() {
      this.contacts$.pipe(
              this.takeUntilDestroyed(),
          ).subscribe((data:any[])=>{
          this.records = this.sortData(data);
      });
   }

  // WARNING - if you declare your ngOnDestroy in the component
  ngOnDestroy() {
     // DO NOT FORGET to call this
     super.ngOnDestroy();
     doYourStuff();
  }

}

其他選項(我的首選)是沒有父抽象 class (盡管也可以這樣實現),而是使用名為subsink的實用程序( npm i subsink --save

import { SubSink } from 'subsink';

export class SampleComponent implements OnInit, OnDestroy {
  private subs = new SubSink();

  ngOnInit(): void {
    // Just put it into sink.
    this.subs.sink = this.contacts$.subscribe((data:any[])=>{
      this.records = this.sortData(data);
    });
    // call repeatedly
    this.subs.sink = this.otherService$.subscribe((data:any[])=>{
      this.things = this.sortData(data);
    });
  }

  ngOnDestroy(): void {
    // this will unsubscribe all
    this.subs.unsubscribe();
  }
}

不會。HttpClient 返回一個冷的 observable,它在第一個訂閱者加入時觸發一次,並在 web 服務調用返回或錯誤時完成。

是的,總是退訂。 您有多種退訂方式,具體如下:

- 使用takeUntil()

- take(1)

- ngOnDestroy() unsubscribe() )

- 使用async pipe

是的,httpClient 返回一個冷的 observable,但是這個問題將解釋你為什么仍然應該unsubscribe (看第二個得票最高的答案)

是否有必要取消訂閱由 Http 方法創建的 observables?

https://blog.angularindepth.com/why-you-have-to-unsubscribe-from-observable-92502d5639d0

旁注:

1) 切勿訂閱服務。 我們處於 Angular 世界中,我們需要以一種被動的方式思考,在服務中購買訂閱會阻止您使用該 observable,以防您想將其與其他東西結合使用。

2) 在使用 observables 時開始使用聲明式方法。

3)停止使用any ,這不是一個好習慣。 使用類和接口,這樣您的代碼將更具可讀性。

聲明式方法的好處: -利用 RxJs 可觀察對象和操作符的強大功能 -有效組合流 -輕松共享可觀察對象 -輕松響應用戶操作

您可能想知道聲明性方法是什么樣的?

您將這樣做,而不是返回 observables 的方法。

服務.TS

  yourObservableName$ = this.httpClient.get('/contacts', {headers: headers})
              .pipe(timeout(this.authService.getTimeoutLimit('normal')));

組件.TS

this.httpService.yourObservableName$.subscribe(...)

正如許多人已經指出的那樣,http 返回一個 Cold Observable,但您仍然應該取消訂閱 observable。 我會建議一種更好的方式來管理退訂,這樣您就不必每次都手動添加 ngOnDestroy() 生命周期掛鈎。 我將首先創建一個取消訂閱組件,如下所示

import { OnDestroy } from "@angular/core";
import { Subject } from "rxjs";

export abstract class UnsubscribeComponent implements OnDestroy {
  protected destroyed$: Subject<void> = new Subject();

  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }
}

然后在每個組件 class 聲明中擴展它(如果需要)

export class ABC extends UnsubscribeComponent

在構造函數中調用 super()

constructor(your dependencies) {
  super()
}

最后使用您的訂閱,執行以下操作

this.obs$.pipe(takeUntil(this.destroyed$)).subscribe(() => {
  // some logic here
})

另一件事是 takeUntil 應該是 pipe 中的最后一個運算符。 希望這對您有所幫助。

是的,取消訂閱 ngOnDestroy 生命周期方法中的所有訂閱是一個好習慣,您可以通過參考 class 級別變量中的每個訂閱然后手動取消訂閱來完成!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM