在 Angular 中使用 RxJS Subject、BehaviourSubject、ReplaySubject、AsyncSubject 或 Void Subject 时

2025-06-04

在 Angular 中使用 RxJS Subject、BehaviourSubject、ReplaySubject、AsyncSubject 或 Void Subject 时

Angular 提供了多种类型的 Observable 供您使用。您可能在 Angular 示例中见过 Subject、BehaviourSubject、ReplaySubject 或 AsyncSubject,并且想知道它们是什么以及何时可以使用它们。

在这篇文章中,我想深入探讨一下这些 Subject 的类型以及应该在什么时候使用它们。所以,系好安全带,享受旅程吧。

divider-byrayray.png

目录

divider-byrayray.png

什么是主题?

RxJS 负责 Angular 中的响应式。Subject 是 RxJS 库中一种特殊的 Observable 类型。

如果您不知道什么是 Observable,请查看LogRocket 博客上的这篇文章“理解 RxJS Observables 以及为什么需要它们”。

单播

单播示例

Observable 是单播的。
观察者和它的订阅者是一对一的关系。每个订阅的观察者都拥有一个独立的 Observable 执行。

多播

多播示例

与常规的可观察对象 (Observable) 相比,Subject 允许将值多播给多个观察者 (Observer)。Subject 与其订阅者之间存在一对多的关系。

一个 Subject 既可以是 Observable,也可以是 Observer。它拥有一个注册表,其中包含多个 Observable 的监听器。

divider-byrayray.png

代码中的 Observable 和 Subject

Observable 和 Subject 共享 API。它们都拥有相同的方法以及创建方法。但它们的行为却截然不同。

代码中可观察

import { Observable } from "rxjs"

const observable = new Observable(subscriber => {
    subscriber.next(1);
    subscriber.next(2);
    subscriber.next(3);
    subscriber.complete();
});

console.log('just before subscribe');

// Subscriber 1
observable.subscribe({
  next(x) { console.log('sub1: got value ' + x); },
  error(err) { console.error('sub1: something wrong occurred: ' + err); },
  complete() { console.log('sub1: done'); }
});

// Subscriber 2
observable.subscribe({
  next(x) { console.log('sub2: got value ' + x); },
  error(err) { console.error('sub2: something wrong occurred: ' + err); },
  complete() { console.log('sub2: done'); }
});

console.log('just after subscribe');
Enter fullscreen mode Exit fullscreen mode

工作示例

在这里,您可以看到数据已发送给第一个订阅者,并且在继续发送给下一个订阅者之前将完成。

RxJS 文档中,他们描述了“每次调用都会observable.subscribe为给定的订阅者触发其独立的设置”。

这就是为什么每个订阅者都彼此独立运行。但是 RxJS 团队提供了一种创建“多播 Obsevables ”的方法。

代码中的主题

import { Subject } from "rxjs"

const subject = new Subject();

 // Subscriber 1
subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);

// Subscriber 2
subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(2);
Enter fullscreen mode Exit fullscreen mode

工作示例

通过 Subject,你可以看到 Subject 占据了主导地位。它向两个订阅者发送消息,而不是等待。在我看来,这清楚地展示了普通 Observable 和 Subject 之间的区别。

RxJS 文档订阅主题有如下说明。

在 Subject 内部,subscribe 不会调用新的执行来传递值。它只是将指定的观察者注册到观察者列表中,类似于 addListener 在其他库和语言中的工作方式。

divider-byrayray.png

主题

我们知道 aSubject是一个Observable。但是,它们不是将信息发送给一个订阅者,而是可以同时将数据发送给多个订阅者(即多播)。

ASubject有三种方法可供使用。

  • subscribe通过此方法,您可以激活新订阅者的订阅。
  • next使用此方法,您可以传递新值。所有当前订阅者都将收到此值。
  • complete通过这种方法,您可以关闭对该主题的所有订阅。

一个至关重要的细节是 Subject 没有初始值。通过该next方法传递的每个值都会发送给所有订阅者。

但如果在订阅者订阅之前已经发送了值,则订阅者将不会收到该数据。(点击“运行”按钮查看运行效果

const rxjs = require('rxjs');
const { Subject } = rxjs

const subject = new Subject();

 // Subscriber 1
subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);

// Subscriber 2
subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(2);
Enter fullscreen mode Exit fullscreen mode

工作示例

divider-byrayray.png

行为主体

BehaviourSubject的一个变体Subject。此变体知道当前值,而法线Subject不知道。

当已经向当前订阅者发送了数据时,此 Subject 就变得非常有用。但是稍后会引入另一个订阅者。有时您想将当前值传递给该订阅者。BehaviourSubject您可以使用 来实现。(点击“运行”按钮查看运行效果

import { BehaviorSubject } from "rxjs"

const subject = new BehaviorSubject(0); // 0 is the initial value

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(3);
Enter fullscreen mode Exit fullscreen mode

工作示例

因此,使用BehaviourSubject可以向订阅者提供 的最新已知值Observable。但是,如果您想要比之前的值稍大一点的值怎么办?

divider-byrayray.png

重播主题

ReplaySubject确实做到了它所说的。它可以向新订阅者重播固定数量的值。

想象一下,一位 DJ 正在播放一个在线播放列表。但你想回到该播放列表。它ReplaySubject可以确保你可以恢复其中三首曲目,然后从那里开始收听。(点击“运行”按钮即可查看运行效果

import { ReplaySubject } from "rxjs" 

const subject = new ReplaySubject(2); // buffer 3 values for new subscribers

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);
subject.next(3);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(4);
subject.next(5);
Enter fullscreen mode Exit fullscreen mode

工作示例

如您所见,在创建时ReplaySubject(2),我传递了数字 2,这告诉Subject它需要将最后两个值发送给每个新订阅者。

当新的订阅者收到传递的值时,它将与其他订阅者保持同步,这非常好。

但是为了确保它ReplaySubject(10000)不会将常量值传递给每个新订阅者,我们可以给它一个时间限制。下面的示例定义了它可以在内存中保存一百个值并将其传递给新订阅者,但这些值的有效期为 500 毫秒。

const subject = new ReplaySubject(100, 500);
Enter fullscreen mode Exit fullscreen mode

此功能提供了很多可能性,因此请明智地使用它。

divider-byrayray.png

异步主题

当我看到它,AsyncSubject并且发现它只在完成时向订阅者发送最新值时,我想,“我为什么要用它?”。直到我在 Medium 上看到这篇文章

所以这说明,一个AsyncSubject对象非常适合 Ajax 请求。因为大多数 GET 请求只需要等待一个响应,对吧?

import { AsyncSubject } from "rxjs"

const subject = new AsyncSubject();

subject.subscribe({
  next: (v) => console.log(`observerA: ${v}`)
});

subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);

subject.subscribe({
  next: (v) => console.log(`observerB: ${v}`)
});

subject.next(5);
subject.complete();
Enter fullscreen mode Exit fullscreen mode

工作示例

当你点击上面的“运行”按钮时,你会看到AsyncSubject将传递多个值,但只有调用该方法之前的最后一个值complete()会给订阅者。

divider-byrayray.png

无效主题

在大多数使用订阅者的场景中Subject,访问已传递的值是必要的。但是,如果您不需要实际值,而只想挂接到事件中,并且不需要值,该怎么办呢?这时,您可以使用 void 主题。

默认行为Subject就是这样。(单击“运行”按钮即可查看其运行情况

import { Subject } from "rxjs"

const subject = new Subject(); // Shorthand for Subject<void>

subject.subscribe({
  next: () => console.log('One second has passed')
});

setTimeout(() => subject.next(), 1000);
Enter fullscreen mode Exit fullscreen mode

工作示例

divider-byrayray.png

结论

让我们总结一下,看看何时需要常规类型Observable或其中一种Subject类型。

在以下情况下使用 Observable......

当你只需要一个订阅者时,应该使用常规订阅者Observable。或者,你不关心第一个订阅者是否先完成,直到第二个订阅者获取其值为止。

在以下情况下使用主题......

当您需要多个订阅者并关心所有订阅者同时获取其新值时,您需要一个Subject

  • BehaviourSubject当您需要最后给定的值时使用。
  • ReplaySubject当您需要超过最后给定值时使用。 (例如,前五个值)或者您想为可以有效地将值发送给订阅者设置一个时间窗口。
  • AsyncSubject当您只想将最后一个值传递给订阅者时,请使用。
  • Subject如果您不想传递任何值而只想挂接到事件,请使用 Void 。

希望这能帮助您做出正确的选择!

divider-byrayray.png

谢谢!

hashnode-页脚.png
*希望你读完这篇文章后能有所收获,或者受到启发去创作新作品!🤗 如果是这样,请考虑通过电子邮件订阅(滚动到此页面顶部)或在 Hashnode 上关注我。
*

你知道吗,你可以自己创建一个像这样的开发者博客?完全免费。👍💰🎉🥳🔥

如果我还有问题或想回复你,请向下滚动并给我留言。如果你想保持私密,请在推特上给我发私信 @DevByRayRay 。我的私信随时开放😁

文章来源:https://dev.to/devbyrayray/when-use-rxjs-subject-behavioursubject-replaysubject-asyncsubject-or-void-subject-in-angular-4pn9
PREV
升级!提升你的 JavaScript 技能,3 级
NEXT
面试准备资源(前端)。