我一直在试图理解这三个概念:
我想使用它们,并知道何时和为什么使用它们,使用它们的好处是什么,尽管我已经阅读了文档、观看了教程并搜索了Google,但我仍然无法理解。
那么它们的目的是什么?最好有一个实际的案例,甚至不需要编码。
我更希望得到清晰的解释,而不仅仅是“a+b => c,您已订阅......"
谢谢
我一直在试图理解这三个概念:
我想使用它们,并知道何时和为什么使用它们,使用它们的好处是什么,尽管我已经阅读了文档、观看了教程并搜索了Google,但我仍然无法理解。
那么它们的目的是什么?最好有一个实际的案例,甚至不需要编码。
我更希望得到清晰的解释,而不仅仅是“a+b => c,您已订阅......"
谢谢
主要取决于行为和语义。使用:
Subject
- 订阅者只会在订阅之后接收到已发出的值。问问自己,这是否是您想要的?订阅者是否需要了解先前的值?如果不需要,则可以使用此选项;否则,请选择其他选项。例如,在组件与组件之间通信时。假设您有一个组件,它会在按钮单击时为其他组件发布事件。您可以使用具有 subject 的服务进行通信。
BehaviorSubject
- 最新值被缓存。订阅者将在初始订阅时获得最新值。此主题的语义是表示随时间变化的值。例如,已登录用户。初始用户可能是匿名用户。但是一旦用户登录,新值就是经过身份验证的用户状态。
BehaviorSubject
使用初始值初始化。对于编码喜好,这有时很重要。例如,您将其用空值初始化。然后在订阅中,您需要执行空检查。也许可以接受,也可能很烦人。
ReplaySubject
- 它可以缓存指定数量的发射。任何订阅者都将在订阅时获取所有缓存的值。何时需要此行为?老实说,我没有任何需要此行为的情况,除了以下情况:
如果您使用缓冲区大小为 1
初始化 ReplaySubject
,则它实际上就像一个 BehaviorSubject
一样表现。最后一个值始终被缓存,因此它就像随时间变化的值。在这种情况下,就不需要像使用null初始化 BehaviorSubject
的情况那样进行 null 检查。在这种情况下,直到第一次发布之前,订阅者都不会收到任何值。
所以,你应该根据你期望的行为选择使用哪种(Subject)。大部分情况下,你可能想使用 BehaviorSubject
,因为你真正想要表示的是“随着时间变化的值”的语义。但我个人认为,使用初始化为 1
的 ReplaySubject
替代也没什么问题。
你需要避免使用普通的 Subject
,当你实际上需要的是一些缓存行为。例如,你正在编写一个路由守卫或一个解析器。在该守卫中,你获取了一些数据并将其设置在服务的 Subject
中。然后在路由组件中,你订阅该服务主题,试图获取在守卫中发出的那个值。糟糕了,值在哪里?它已经被发出了,呃。使用“缓存”类型的 subject!
ReplaySubject
并且缓冲区大小为1正是我所需要的。我有一个路由守卫需要这个值,但需要等待第一次发射。因此,BehaviorSubject
不太适合,因为我不想要初始值(null
也不行,因为我正在使用它来表示状态)。 - Andrew M.ReplaySubject
,它实际上就像一个BehaviorSubject
一样运作,并不完全正确。请查看这篇优秀的博客文章以获取这两者之间的差异。例如,如果您订阅一个已完成的BehaviorSubject
,则您将不会收到最后一个值,但对于ReplaySubject(1)
,您将收到最后一个值。 - Wiltconst mySubject = new Rx.Subject();
mySubject.next(1);
const subscription1 = mySubject.subscribe(x => {
console.log('From subscription 1:', x);
});
mySubject.next(2);
const subscription2 = mySubject.subscribe(x => {
console.log('From subscription 2:', x);
});
mySubject.next(3);
subscription1.unsubscribe();
mySubject.next(4);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.min.js"></script>
From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4
const mySubject = new Rx.ReplaySubject(2);
mySubject.next(1);
mySubject.next(2);
mySubject.next(3);
mySubject.next(4);
mySubject.subscribe(x => {
console.log('From 1st sub:', x);
});
mySubject.next(5);
mySubject.subscribe(x => {
console.log('From 2nd sub:', x);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.min.js"></script>
From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
const mySubject = new Rx.BehaviorSubject('Hey now!');
mySubject.subscribe(x => {
console.log('From 1st sub:', x);
});
mySubject.next(5);
mySubject.subscribe(x => {
console.log('From 2nd sub:', x);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.min.js"></script>
From 1st sub: Hey now!
From 1st sub: 5
From 2nd sub: 5
这是一个关于不同可观察类型的方便总结,命名可能有点不直观哈哈.
Subject
- 订阅者只能在订阅后获取发布的值。BehaviorSubject
- 新的订阅者会在订阅立即获得上一个已发布的值或初始值。ReplaySubject
- 新的订阅者会在订阅立即获得所有之前发布的值。"如果您使用大小为1的缓冲区初始化ReplaySubject
,那么它实际上的行为就像一个BehaviorSubject
一样"
这并不完全正确;请查看这篇伟大的博客文章,了解这两者之间的区别。例如,如果您订阅已完成的BehaviorSubject
,则不会接收到最后一个值,但对于ReplaySubject(1)
,您将接收到最后一个值。
这是一个重要的差异,不应忽视:
const behavior = new BehaviorSubject(null);
const replay = new ReplaySubject(1);
behavior.skip(1).subscribe(v => console.log('BehaviorSubject:', v));
replay.subscribe(v => console.log('ReplaySubject:', v));
behavior.next(1);
behavior.next(2);
behavior.complete();
behavior.subscribe(v => console.log('Late B subscriber:', v));
replay.next(1);
replay.next(2);
replay.complete();
replay.subscribe(v => console.log('Late R subscriber:', v));
来自Randall Koutnik的书“使用RxJS构建反应式网站”:
Subject是一个强化版的可观察对象。在其核心部分,Subject与常规的可观察对象类似,但每个订阅都连接到同一源。此外,Subject也是观察者,并具有next、error和done方法,可以向所有订阅者同时发送数据。由于Subject是观察者,它们可以直接传递到subscribe调用中,并通过主题将来自原始可观察对象的所有事件发送给其订阅者。
我们可以使用ReplaySubject来跟踪历史记录。一个ReplaySubject记录最后n个事件并将它们回放到每个新的订阅者。例如,在聊天应用程序中,我们可以使用它来跟踪以前的聊天记录。
BehaviorSubject是ReplaySubject的简化版本。 ReplaySubject存储任意数量的事件,而BehaviorSubject仅记录最新事件的值。每当BehaviorSubject记录新的订阅时,它会将最新的值以及任何传递的新值发送给订阅者。当处理单个状态单元(例如配置选项)时,BehaviorSubject非常有用。
正如一些帖子中提到的那样,被接受的答案是错误的,因为 BehaviorSubject != ReplaySubject(1)
,这不仅仅是编码风格的偏好。
在评论中经常提到“guards”,这也是我最经常发现使用回放主题的用例的地方。更具体地说,如果您有一个类似于take(1)
的场景,并且您不仅想要获取初始值。
例如,请查看以下内容:
ngOnInit() {
const behaviorSubject = new BehaviorSubject<boolean>(null);
const replaySubject = new ReplaySubject<boolean>(1);
this.checkLoggedIn(behaviorSubject, 'behaviorSubject');
this.checkLoggedIn(replaySubject, 'replaySubject');
behaviorSubject.next(true);
replaySubject.next(true);
}
checkLoggedIn($userLoggedIn: Observable<boolean>, id: string) {
$userLoggedIn.pipe(take(1)).subscribe(isLoggedIn => {
if (isLoggedIn) {
this.result[id] = 'routed to dashboard';
} else {
this.result[id] = 'routed to landing page';
}
});
}
结果如下:
{
"behaviorSubject": "routed to landing page",
"replaySubject": "routed to dashboard"
}
ReplaySubject
!工作代码:https://stackblitz.com/edit/replaysubject-vs-behaviorsubject?file=src%2Fapp%2Fapp.component.ts
另一个区别是你可以使用BehaviorSubject
的值获取器来获取当前值。在某些情况下,这非常有用,当你只需要当前值时。例如,当用户单击某个东西并且你仅需要该值一次时。在这种情况下,你不需要订阅然后突然取消订阅。唯一需要的是:
BehaviorSubject bSubject = new BehaviorSubject<IBasket>(basket);
getCurrentBasketValue() {
return this.bSubject.value;
}
// ***********Subject concept ***********
let subject = new Subject<string>();
subject.next("Eureka");
subject.subscribe((data) => {
console.log("Subscriber 1 got data >>>>> "+ data);
});
subject.subscribe((data) => {
console.log("Subscriber 2 got data >>>>> "+ data);
});
// ********behaviour subject*********
// Behavior subjects need a first value
let subject1 = new BehaviorSubject<string>("First value");
subject1.asObservable().subscribe((data) => {
console.log("First subscriber got data behaviour subject>>>>> "+ data);
});
subject1.next("Second value")