RxJS 中 Subject 是一种特殊类型的 Observable,它允许将值多播给多个观察者,所以 Subject 是多播的,而普通的 Observables 是单播的(每个已订阅的观察者都拥有 Observable 的独立执行)。
代码中的定义:
export declare class Subject<T> extends Observable<T> implements SubscriptionLike {}
在 RxJS 中常用有四种 Subject 分别是:普通Subject,BehaviorSubject,AsyncSubject,ReplaySubject。
普通Subject
普通 Subject 示例:
let subject: Subject<number> = new Subject<number>();
let count = 0;
let intervalId = setInterval(() => {
count++;
if (count < 3) {
subject.next(count);
} else {
subject.complete();
clearInterval(intervalId);
}
}, 1000);
subject.subscribe(
data => { console.log(`1st subscribe: ${data}`) },
(err) => { console.log(`1st subscribe err: ${err}`) },
() => { console.log(`1st subscribe complete`) });
setTimeout(() => {
subject.subscribe(
data => { console.log(`2nd subscribe: ${data}`) },
(err) => { console.log(`2nd subscribe err: ${err}`) },
() => { console.log(`2nd subscribe complete`) });
}, 2000);
setTimeout(() => {
subject.subscribe(
data => { console.log(`3rd subscribe: ${data}`) },
(err) => { console.log(`3rd subscribe err: ${err}`) },
() => { console.log(`3rd subscribe complete`) });
}, 5000);
运行结果:
可以看到效果与 Hot Observables 一样, 在某个值被发出之后再添加的订阅,不能接收到该值及其之前发出的值。但是能收到完成通知。因此 Subject 是一种 Hot Observable。
BehaviorSubject
Subject 的其中一个变体是 BehaviorSubject,它有一个“当前值”的概念。它保存了发送给消费者的最新值。并且当有新的观察者订阅时,会立即从 BehaviorSubject 那接收到“当前值”。
let subject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
let count = 0;
let intervalId = setInterval(() => {
count++;
if (count < 3) {
subject.next(count);
} else {
subject.complete();
clearInterval(intervalId);
}
}, 1000);
subject.subscribe(
data => { console.log(`1st subscribe: ${data}`) },
(err) => { console.log(`1st subscribe err: ${err}`) },
() => { console.log(`1st subscribe complete`) });
setTimeout(() => {
subject.subscribe(
data => { console.log(`2nd subscribe: ${data}`) },
(err) => { console.log(`2nd subscribe err: ${err}`) },
() => { console.log(`2nd subscribe complete`) });
}, 2100);
setTimeout(() => {
subject.subscribe(
data => { console.log(`3rd subscribe: ${data}`) },
(err) => { console.log(`3rd subscribe err: ${err}`) },
() => { console.log(`3rd subscribe complete`) });
}, 5000);
运行结果:
可以看到,第2个订阅在主体发出2之后进行添加也接收到了数值2(“当前值”)。
为了更能体现与一般Subject的区别,可以将以上代码中的 subject.complete(); 注释掉,认为 Subject 未完成数据的推送。
此时 使用一般Subject 的运行结果:
第2个和第3个订阅添加之后,主体未发出数据,所以都没有打印。
使用BehaviorSubject 的运行结果:
第2个和第3个订阅添加之后,主体未发出数据,但是都接收到了主体的当前值(最后一次发出的数据),所以都有打印2。
ReplaySubject
ReplaySubject 类似于 BehaviorSubject,它可以发送旧值给新的订阅者,但它还可以记录 Observable 执行的一部分。
let subject: ReplaySubject<number> = new ReplaySubject<number>(2);
let count = 0;
let intervalId = setInterval(() => {
count++;
if (count < 5) {
subject.next(count);
} else {
subject.complete();
clearInterval(intervalId);
}
}, 1000);
subject.subscribe(
data => { console.log(`1st subscribe: ${data}`) },
(err) => { console.log(`1st subscribe err: ${err}`) },
() => { console.log(`1st subscribe complete`) });
setTimeout(() => {
subject.subscribe(
data => { console.log(`2nd subscribe: ${data}`) },
(err) => { console.log(`2nd subscribe err: ${err}`) },
() => { console.log(`2nd subscribe complete`) });
}, 4000);
setTimeout(() => {
subject.subscribe(
data => { console.log(`3rd subscribe: ${data}`) },
(err) => { console.log(`3rd subscribe err: ${err}`) },
() => { console.log(`3rd subscribe complete`) });
}, 8000);
运行结果:
可以看到后面添加的订阅都接收到了最近推出的2个数据值,而且都收到了完成通知。
AsyncSubject
AsyncSubject 是另一个 Subject 变体,只有当 Observable 执行完成时(执行 complete()),它才会将执行的最后一个值发送给观察者。
let subject: AsyncSubject<number> = new AsyncSubject<number>();
let count = 0;
let intervalId = setInterval(() => {
count++;
if (count < 5) {
subject.next(count);
} else {
subject.complete();
clearInterval(intervalId);
}
}, 1000);
subject.subscribe(
data => { console.log(`1st subscribe: ${data}`) },
(err) => { console.log(`1st subscribe err: ${err}`) },
() => { console.log(`1st subscribe complete`) });
setTimeout(() => {
subject.subscribe(
data => { console.log(`2nd subscribe: ${data}`) },
(err) => { console.log(`2nd subscribe err: ${err}`) },
() => { console.log(`2nd subscribe complete`) });
}, 3000);
setTimeout(() => {
subject.subscribe(
data => { console.log(`3rd subscribe: ${data}`) },
(err) => { console.log(`3rd subscribe err: ${err}`) },
() => { console.log(`3rd subscribe complete`) });
}, 6000);
运行结果:
可以看到,所有订阅都只接收到了主体完成前发出的最后一个数据。