Angular 中的主题与行为主题与重播主题
Subject vs BehaviorSubject vs ReplaySubject in Angular
我一直想了解这 3 个:
我想使用它们并知道何时以及为什么使用它们,使用它们有什么好处,尽管我已经阅读了文档、观看了教程并进行了搜索 google 但我没有任何意义
那么他们的目的是什么?一个真实的案例将不胜感激,它甚至不必编写代码。
我希望有一个清晰的解释,而不仅仅是“a+b => c 你订阅了....”
谢谢
这真的归结为行为和语义。用
Subject
- 订阅者只会获得在订阅 之后发出的发布值。问问自己,这是你想要的吗?订户是否需要了解有关先前值的任何信息?如果没有,那么你可以使用这个,否则选择其他之一。例如,组件到组件的通信。假设您有一个组件,可以在单击按钮时为其他组件发布事件。您可以使用带有主题的服务进行交流。
BehaviorSubject
- 最后一个值被缓存。订阅者将在初始订阅时获得最新值。该主题的语义是表示随时间变化的值。例如登录用户。初始用户可能是匿名用户。但是一旦用户登录,新值就是经过身份验证的用户状态。
BehaviorSubject
用初始值初始化。这有时对编码偏好很重要。比如说你用 null
初始化它。然后在您的订阅中,您需要进行空检查。也许还好,也许烦人。
ReplaySubject
- 它最多可以缓存指定数量的发射。任何订阅者都将在订阅时获得所有缓存的值。你什么时候需要这种行为?老实说,我不需要这样的行为,除了以下情况:
如果您初始化 ReplaySubject
缓冲区大小为 1
,那么它实际上 表现 就像 BehaviorSubject
。最后一个值总是被缓存,所以它就像一个随时间变化的值。有了这个,就不需要像 BehaviorSubject
用 null
初始化的情况那样进行 null
检查。在这种情况下,在第一次发布之前,不会向订阅者发送任何值。
所以这真的取决于您期望的行为(至于使用哪一个)。大多数时候你可能想要使用 BehaviorSubject
因为你真正想要表示的是 "value over time" 语义。但我个人认为用 1
初始化的 ReplaySubject
的替换没有任何问题。
你想要避免的是在你真正需要的是一些缓存行为时使用香草Subject
。举个例子,你正在写一个路由守卫或一个解决方案。您在该守卫中获取一些数据并将其设置在服务 Subject
中。然后在路由组件中,您订阅服务主题以尝试获取在守卫中发出的值。哎呀。价值在哪里?它已经发出了,DUH。使用 "caching" 主题!
另请参阅:
- What are RxJS Subject's and the benefits of using them?
不同可观察类型的方便总结,我知道的非直观命名大声笑。
Subject
- 订阅者只会在订阅后获得发布的值。
BehaviorSubject
- 新订阅者在订阅后立即获得最后发布的值或初始值。
ReplaySubject
- 新订阅者在订阅后立即获得所有先前发布的值
来自:Randall Koutnik 的书“使用 RxJS 构建响应式网站”。 :
A Subject 是一个涡轮增压可观察对象。在它的核心,一个 Subject 的行为很像一个常规的可观察对象,但每个订阅都连接到同一个源。 Subjects 也是观察者,并且有 next、error 和 done 方法一次性向所有订阅者发送数据。因为 subjects 是观察者,所以它们可以直接传递到 subscribe 调用中,并且来自原始 observable 的所有事件都将通过 subject 发送给它的订阅者。
我们可以使用 ReplaySubject 来追踪历史。 ReplaySubject 记录最后 n 个事件并将它们回放给每个新订阅者。例如在聊天应用程序中。我们可以用它来追踪之前的聊天记录。
A BehaviorSubject 是 ReplaySubject 的简化版本。
ReplaySubject存储了任意数量的事件,BehaviorSubject只记录了最新事件的值。每当 BehaviorSubject 记录新订阅时,它会向订阅者发送最新值以及传入的任何新值。 BehaviorSubject 是在处理单个状态单元时很有用,例如配置选项。
// ***********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")
- 主题 - 订阅者只会在订阅后获得发布的值。
- BehaviorSubject - 新订阅者在订阅后立即获得最后发布的值或初始值。
- Subject:在订阅时它总是获取订阅后推送的数据,即未收到先前推送的值.
const 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
Note how subscriptions that arrive late are missing out on some of the data that’s been pushed into the subject.
- 重播主题:可以通过保留一个先前值的缓冲区来提供帮助发送到新订阅。
这是重放主题的用法示例,其中 buffer of 2 previous values
被保留并在新订阅上发出:
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
投票最多的答案显然是错误的:
"If you initialize a ReplaySubject
with a buffer size of 1, then it actually behaves just like a BehaviorSubject
"
这不完全正确;检查 this great blog post 这两者之间的区别。例如,如果您订阅已完成的 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));
查看此主题的代码示例 here which comes from another great blog post。
正如一些帖子中提到的,接受的答案是错误的,因为 BehaviorSubject != ReplaySubject(1)
这不仅仅是编码风格的偏好。
在评论中经常提到“守卫”,这也是我最常发现重播主题用例的地方。更具体地说,如果您有类似 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
我一直想了解这 3 个:
我想使用它们并知道何时以及为什么使用它们,使用它们有什么好处,尽管我已经阅读了文档、观看了教程并进行了搜索 google 但我没有任何意义
那么他们的目的是什么?一个真实的案例将不胜感激,它甚至不必编写代码。
我希望有一个清晰的解释,而不仅仅是“a+b => c 你订阅了....”
谢谢
这真的归结为行为和语义。用
Subject
- 订阅者只会获得在订阅 之后发出的发布值。问问自己,这是你想要的吗?订户是否需要了解有关先前值的任何信息?如果没有,那么你可以使用这个,否则选择其他之一。例如,组件到组件的通信。假设您有一个组件,可以在单击按钮时为其他组件发布事件。您可以使用带有主题的服务进行交流。BehaviorSubject
- 最后一个值被缓存。订阅者将在初始订阅时获得最新值。该主题的语义是表示随时间变化的值。例如登录用户。初始用户可能是匿名用户。但是一旦用户登录,新值就是经过身份验证的用户状态。BehaviorSubject
用初始值初始化。这有时对编码偏好很重要。比如说你用null
初始化它。然后在您的订阅中,您需要进行空检查。也许还好,也许烦人。ReplaySubject
- 它最多可以缓存指定数量的发射。任何订阅者都将在订阅时获得所有缓存的值。你什么时候需要这种行为?老实说,我不需要这样的行为,除了以下情况:如果您初始化
ReplaySubject
缓冲区大小为1
,那么它实际上 表现 就像BehaviorSubject
。最后一个值总是被缓存,所以它就像一个随时间变化的值。有了这个,就不需要像BehaviorSubject
用null
初始化的情况那样进行null
检查。在这种情况下,在第一次发布之前,不会向订阅者发送任何值。
所以这真的取决于您期望的行为(至于使用哪一个)。大多数时候你可能想要使用 BehaviorSubject
因为你真正想要表示的是 "value over time" 语义。但我个人认为用 1
初始化的 ReplaySubject
的替换没有任何问题。
你想要避免的是在你真正需要的是一些缓存行为时使用香草Subject
。举个例子,你正在写一个路由守卫或一个解决方案。您在该守卫中获取一些数据并将其设置在服务 Subject
中。然后在路由组件中,您订阅服务主题以尝试获取在守卫中发出的值。哎呀。价值在哪里?它已经发出了,DUH。使用 "caching" 主题!
另请参阅:
- What are RxJS Subject's and the benefits of using them?
不同可观察类型的方便总结,我知道的非直观命名大声笑。
Subject
- 订阅者只会在订阅后获得发布的值。BehaviorSubject
- 新订阅者在订阅后立即获得最后发布的值或初始值。ReplaySubject
- 新订阅者在订阅后立即获得所有先前发布的值
来自:Randall Koutnik 的书“使用 RxJS 构建响应式网站”。 :
A Subject 是一个涡轮增压可观察对象。在它的核心,一个 Subject 的行为很像一个常规的可观察对象,但每个订阅都连接到同一个源。 Subjects 也是观察者,并且有 next、error 和 done 方法一次性向所有订阅者发送数据。因为 subjects 是观察者,所以它们可以直接传递到 subscribe 调用中,并且来自原始 observable 的所有事件都将通过 subject 发送给它的订阅者。
我们可以使用 ReplaySubject 来追踪历史。 ReplaySubject 记录最后 n 个事件并将它们回放给每个新订阅者。例如在聊天应用程序中。我们可以用它来追踪之前的聊天记录。
A BehaviorSubject 是 ReplaySubject 的简化版本。 ReplaySubject存储了任意数量的事件,BehaviorSubject只记录了最新事件的值。每当 BehaviorSubject 记录新订阅时,它会向订阅者发送最新值以及传入的任何新值。 BehaviorSubject 是在处理单个状态单元时很有用,例如配置选项。
// ***********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")
- 主题 - 订阅者只会在订阅后获得发布的值。
- BehaviorSubject - 新订阅者在订阅后立即获得最后发布的值或初始值。
- Subject:在订阅时它总是获取订阅后推送的数据,即未收到先前推送的值.
const 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
Note how subscriptions that arrive late are missing out on some of the data that’s been pushed into the subject.
- 重播主题:可以通过保留一个先前值的缓冲区来提供帮助发送到新订阅。
这是重放主题的用法示例,其中 buffer of 2 previous values
被保留并在新订阅上发出:
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
投票最多的答案显然是错误的:
"If you initialize a ReplaySubject
with a buffer size of 1, then it actually behaves just like a BehaviorSubject
"
这不完全正确;检查 this great blog post 这两者之间的区别。例如,如果您订阅已完成的 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));
查看此主题的代码示例 here which comes from another great blog post。
正如一些帖子中提到的,接受的答案是错误的,因为 BehaviorSubject != ReplaySubject(1)
这不仅仅是编码风格的偏好。
在评论中经常提到“守卫”,这也是我最常发现重播主题用例的地方。更具体地说,如果您有类似 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