Subject是一种特殊类型的 Observable,允许将值多播到多个 Observer。
Subjects 就像 EventEmitters:它们维护着许多监听器的注册表。
import { Subject, from } from 'rxjs';
const subject = new Subject<number>();
subject.subscribe({ next: (v) => console.log(`observerA: ${v}`),});
subject.subscribe({ next: (v) => console.log(`observerB: ${v}`),});
const observable = from([1, 2, 3]);
observable.subscribe(subject); // You can subscribe providing a Subject
// Logs:
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2
// observerA: 3
// observerB: 3
多播的 Observable 在底层使用 Subject 来让多个 Observer 看到相同的 Observable 执行过程。
import { from, Subject, multicast } from 'rxjs';
const source = from([1, 2, 3]);
const subject = new Subject();
const multicasted = source.pipe(multicast(subject));
// These are, under the hood, `subject.subscribe({...})`:
multicasted.subscribe({
next: (v) => console.log(`observerA: ${v}`),
});
multicasted.subscribe({
next: (v) => console.log(`observerB: ${v}`),
});
// This is, under the hood, `source.subscribe(subject)`:
multicasted.connect();
multicast 返回一个 ConnectableObservable,它只是一个带有 connect() 方法的 Observable。
connect() 在后台执行 source.subscribe(subject),所以 connect() 返回一个订阅,你可以退订以取消共享的 Observable 执行过程。
手动调用 connect() 并处理订阅通常很麻烦。通常,我们希望在第一个 Observer 抵达时自动连接,并在最后一个 Observer 退订时自动取消共享执行。
e.g. 执行
- 第一个 Observer 订阅了多播的 Observable
- 多播的 Observable 已连接
- next 值 0 被传递给第一个 Observer
- 第二个 Observer 订阅了多播的 Observable
- next 的值 1 被传递给第一个 Observer
- next 的值 1 被传递给第二个 Observer
- 第一个 Observer 退订多播的 Observable
- next 值 2 被传递给第二个 Observer
- 第二个 Observer 退订多播的 Observable
- 与多播的 Observable 的连接被退订
refCount 能使多播的 Observable 在第一个订阅者抵达时自动开始执行,并在最后一个订阅者离开时停止执行。
const refCounted = source.pipe(multicast(subject), refCount());
refCount() 方法只存在于 ConnectableObservable 上,它返回一个 Observable,而不是另一个 ConnectableObservable。
Subjects 的变体之一是 BehaviorSubject,它具有“当前值”的概念。
它存储发送给其消费者的最新值,并且每当有新的 Observer 订阅时,它将立即从 BehaviorSubject 接收到“当前值”
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);
// Logs
// observerA: 0
// observerA: 1
// observerA: 2
// observerB: 2 //第二个 Observer 接收到值 2,即使它是在发送值 2 之后订阅的。
// observerA: 3
// observerB: 3
ReplaySubject 与 BehaviorSubject 类似,它可以将旧值发送给新订阅者,但它也可以记录 Observable 执行结果的一部分。
ReplaySubject 会记录来自 Observable 执行的多个值,并将它们重播给新订阅者。
可以指定要重播的值的数量
import { ReplaySubject } from 'rxjs';
const subject = new ReplaySubject(3); // buffer 3 values for new subscribers
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);
// Logs:
// observerA: 1
// observerA: 2
// observerA: 3
// observerA: 4
// observerB: 2
// observerB: 3
// observerB: 4
// observerA: 5
// observerB: 5
除了缓冲区大小之外,你还可以指定一个以毫秒为单位的窗口时间,以确定记录的值可以存在多长时间。
//使用 100 个元素的大型缓冲区,但窗口时间参数仅为 500 毫秒
const subject = new ReplaySubject(100, 500 /* windowTime */);
仅将 Observable 执行的最后一个值发送给其 Observer,并且仅在执行完成时发送
AsyncSubject 类似于 last() 操作符,因为它会等待 complete 通知以传递单个值。
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();
// Logs:
// observerA: 5
// observerB: 5
有时,发出值这件事本身比所发出的值更重要。
通过声明一个 void 主体,你可以表明该值是无所谓的。只有事件本身才重要。
import { Subject } from 'rxjs';
// const subject = new Subject();
const subject = new Subject(); // Shorthand for Subject
subject.subscribe({
next: () => console.log('One second has passed'),
});
setTimeout(() => subject.next(), 1000);
在版本 7 之前,Subject 值的默认类型是 any。
Subject 禁用发出值的类型检查,而 Subject 可以防止意外访问所发出的值。