Announcement

👇Official Account👇

图片

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Subscription 订阅与内存管理

Subscription 对象代表一个正在执行的 Observable。它最核心的功能就是通过 unsubscribe() 方法来清理资源和停止 Observable 的执行。在任何一个严肃的应用中,妥善地管理订阅是防止内存泄漏的关键

手动取消订阅

最基本的方式是在组件销毁时手动调用 unsubscribe()

typescript
import { interval, Subscription } from 'rxjs';

class MyComponent {
  private subscription: Subscription;

  constructor() {
    this.subscription = interval(1000).subscribe(console.log);
  }

  destroy() {
    // 在组件销毁时必须取消订阅,否则 interval 会永远执行下去
    this.subscription.unsubscribe();
  }
}

这种方式在只有一两个订阅时还行,但很快就会变得繁琐和容易出错。更好的方式是使用 add() 方法将所有订阅聚合到一个父 Subscription 中,然后在销毁时只调用一次 unsubscribe()

typescript
const parentSubscription = new Subscription();
parentSubscription.add(obs1$.subscribe(...));
parentSubscription.add(obs2$.subscribe(...));

// 在销毁时
parentSubscription.unsubscribe();

声明式取消订阅 (最佳实践)

手动管理 Subscription 对象是命令式的,容易遗漏。在现代前端框架(如 Angular, React)的组件化体系中,最佳实践是使用 takeUntil 操作符进行声明式地取消订阅。

核心思想:

  1. 创建一个 Subject,我们称之为 destroy$
  2. 在组件销毁的生命周期钩子中,调用 destroy$.next()destroy$.complete()
  3. 在组件内的每一个订阅流的 pipe末尾,加上 takeUntil(this.destroy$)

takeUntil 会持续监听 destroy$,一旦 destroy$ 发出任何值或完成,takeUntil 就会让主数据流也完成,从而自动取消订阅并清理资源。

示例 (以类 Angular 组件为例):

typescript
import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

class MyComponent {
  // 1. 创建一个 Subject 用于广播销毁事件
  private destroy$ = new Subject<void>();

  constructor() {
    interval(1000).pipe(
      // 2. 在所有其他操作符之后,使用 takeUntil
      takeUntil(this.destroy$)
    ).subscribe(console.log);

    fromEvent(document, 'click').pipe(
      takeUntil(this.destroy$)
    ).subscribe(console.log);
  }

  // 3. 在组件销毁时,发出信号
  ngOnDestroy() { // 这是 Angular 的销毁钩子
    this.destroy$.next();
    this.destroy$.complete();
  }
}

为什么这是最佳实践?

  • 声明式: 你声明了流的"生命周期",而不是命令式地去销毁它。
  • 不易遗漏: 只需要记住在 pipe 的末尾加上 takeUntil 即可,比管理多个 Subscription 对象更简单。
  • 代码更干净: 模板代码(boilerplate)更少,逻辑更清晰。
  • 适用于任何流: 无论是 interval 还是 fromEvent,都可以用同样的方式管理。

在开发复杂的、长生命周期的应用时,养成使用 takeUntil 的习惯是每个资深 RxJS 开发者必备的技能。

上次更新于: