所有的 RxJS 订阅都需要取消订阅吗?

时间:2021-07-22 23:55:17

标签: rxjs

我在角度组件中有以下代码来捕获 keyup 事件并在发生这种情况时做出响应。用户可以离开页面,返回并执行数百次相同的操作。

    fromEvent(this.input?.nativeElement, 'keyup')
      .pipe(
        pluck<unknown, string>('target', 'value'),
        filter((searchTerm: string) => (searchTerm?.length > 2 || searchTerm?.length == 0)),
        throttleTime(200),
        debounceTime(300),
        distinctUntilChanged()
      )
      .subscribe(search => {
        this.setPageIndex();
        this.TriggerLoadUsers(search, 'asc', 0, 10);
      });

这是另一种模式,其中 Subscription 的显式分配已完成,然后在 angular 生命周期方法的 ngOnDestroy 中取消订阅。

public keyupEventsSub$!: Subscription;

this.keyupEventsSub$ = fromEvent(this.input?.nativeElement, 'keyup')
      .pipe(
        pluck<unknown, string>('target', 'value'),
        filter((searchTerm: string) => (searchTerm?.length > 2 || searchTerm?.length == 0)),
        throttleTime(200),
        debounceTime(300),
        distinctUntilChanged()
      )
      .subscribe(search => {
        this.setPageIndex();
        this.TriggerLoadUsers(search, 'asc', 0, 10);
      });

this.keyupEventsSub$.unsubscribe();
  1. 遵循明确分配 Subscriptionsubscribedunsubscribed 的第二种模式是否有优势?
  2. 对任何 Observable 订阅使用相同的模式有什么副作用吗?
  3. 在不需要显式赋值的情况下,是否有更好的模式?

2 个答案:

答案 0 :(得分:1)

1.) 是的,应该取消订阅所有订阅以防止内存泄漏。您不必取消订阅 Http 调用或路由器事件,因为它们是一个并且已经完成并且 Angular 会为我们处理它,但我个人仍然取消订阅所有订阅。

2.) 对任何可观察订阅使用相同的模式没有副作用。模式很多,我会在最后展示。

3.) 有一个更好的模式,我将从最不喜欢到最喜欢。

直接订阅分配。这样做的缺点是每个可观察流都有许多订阅变量,因此可能会失控。

// Direct subscription variable (What you have shown)
// don't put a dollar at the end of subscription variable because
// it is a subscription and not an observable
public subscription!: Subscription;
....
this.subscription = this.observable$.subscribe(...);
...
ngOnDestroy(): void {
  this.subscription.unsubscribe();
}

订阅数组: 将每个订阅添加到数组中。

public subscriptions!: Subscription[];
...
this.subscriptions.push(this.observable$.subscribe(...));
...
ngOnDestroy(): void {
  this.subscriptions.forEach(subscription => subscription.unsubscribe());
}

异步管道: 我的最爱之一,但只能在 HTML 中呈现数据时使用,不能用于事件侦听器(本质上意味着每次 observable 发出时都做出反应)。 当视图呈现时,observable 将自动被订阅,一旦视图被销毁,订阅将被取消订阅。

count$ = this.otherObservable$.pipe(map(data => data.count));
...
<h1>{{ count$ | async }}</h1>

破坏主题: 另一个我最喜欢的,这个适用于 TypeScript 类中的订阅(用于事件侦听器)。这种方法的美妙之处在于不会创建太多变量,而且您不必处理数组。

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
....
private destructionSubject$ = new Subject<void>();
...
observable$.pipe(
  takeUntil(this.destructionSubject$),
).subscribe(...);
observable2$.pipe(
  takeUntil(this.destructionSubject$),
).subscribe(...);
...
ngOnDestroy(): void {
  this.destructionSubject$.next();
  this.destructionSubject$.complete();
}

如果您只关心第一次排放而不是后续排放,还有另一种方式:

这可用于事件侦听器(每次此 observable 发出时都做出反应)。这将进行第一次发射并自动取消订阅(订阅已失效)。

import { take } from 'rxjs/operators';
....
observable$.pipe(take(1)).subscribe(...);

我希望我回答了您的所有问题,并为您提供了退订的好方法。

答案 1 :(得分:1)

是否应该取消订阅所有 RxJS 订阅?

只有可能永远不会Observableserrorcomplete 需要取消订阅。如果您不确定,最好取消订阅。

from(promise) 保证完成或出错。
from(['a','r','r','a','y']) 保证完成。
of(...) 保证完成。
EMPTY 保证完成。
NEVER 永远不会完成或失败。
fromEvent(...) 可能永远不会完成或失败。
http.get(...) 一个写得很好的 http 客户端应该总是完成或最终失败,但有一些(出于各种技术原因)不会。如果您不确定,请退订。

如何取消订阅

总的来说,隐式优于显式。有多种运营商会在满足特定条件时为您退订。

take,
takeWhile
takeUntil

是其中最受欢迎的 3 个。与其在我们的代码中的某处粘贴 stream.unsubscribe() 相比,他们更喜欢它们。

这样做可以将所有关于可观察对象的逻辑集中在一个地方。随着您使用的 observable 数量的增加,维护/扩展变得更加容易。