Angular中的Subject vs BehaviorSubject vs ReplaySubject


122

我一直想了解那些3:

主题行为主题重播主题。我想使用它们,并且知道何时以及为什么使用它们的好处是什么,尽管我已经阅读了文档,观看了教程并搜索了google,但我对此一无所知。

那么他们的目的是什么?最好是一个真实的案例,它甚至不必编写代码。

我希望给出一个清晰的解释,而不仅仅是“ a + b => c您已订阅...。”

谢谢


1
行为主体已经存在一个可观察的问题。stackoverflow.com/questions/39494058/...和重播主题文档清晰IMO github.com/Reactive-Extensions/RxJS/blob/master/doc/api/...
EKO

这个答案对Rxjs中的主题进行了比较全面的介绍,很好地补充了peeksilet的答案。这还包括有关终止后行为的重要细节,因此很高兴了解一下。
user3743222

Answers:


277

它实际上归结为行为和语义。用

  • Subject-订阅者将仅获得订阅发出的发布值。问问自己,那是你想要的吗?订户是否需要了解有关先前值的任何信息?如果没有,则可以使用此选项,否则请选择其他选项之一。例如,使用组件间通信。假设您有一个组件,单击一次按钮即可发布其他组件的事件。您可以将服务与主题进行通信。

  • BehaviorSubject-缓存了最后一个值。首次订阅时,订户将获得最新价值。该主题的语义是表示随时间变化的值。例如,一个登录用户。初始用户可能是匿名用户。但是,一旦用户登录,那么新值就是经过身份验证的用户状态。

    BehaviorSubject被初始化为一个初始值。这有时对编码首选项很重要。举例来说,您使用初始化了它null。然后在您的订阅中,您需要做一个空检查。也许还可以,或者令人讨厌。

  • ReplaySubject-它可以缓存多达指定数量的排放物。任何订阅者都将在订阅时获得所有缓存的值。什么时候需要这种行为?老实说,除了以下情况外,我不需要这种行为:

    如果您ReplaySubject使用大小为的缓冲区初始化a 1,则它实际上的行为就像BehaviorSubject。最后一个值始终会被缓存,因此它就像一个随时间变化的值。这样,就不需要null像用BehaviorSubject初始化的情况那样进行检查null。在这种情况下,直到第一次发布,才有值传递给订阅者。

因此,这实际上取决于您期望的行为(使用哪种行为)。大多数时候,您可能会想使用a,BehaviorSubject因为您真正想代表的是“随时间推移的价值”语义。但是我个人没有看到用替换了ReplaySubject初始化有什么问题1

当真正需要的是某些缓存行为时,您要避免使用香草Subject。例如,您正在编写路由防护或解决方案。您在该防护中获取一些数据并将其设置在服务中Subject。然后,在路由的组件中,您预订了服务主体,以尝试获取在防护中发出的值。糟糕!价值在哪里?它已经发出了,DUH。使用“缓存”主题!

也可以看看:


1
这是简短且易于理解的差异。当服务中的值更改并且组件也更改该值显示时,则可以使用BehaviourSubjects或Replay Subject。
撒耶夫·法鲁克

1
谢谢!ReplaySubject缓冲区大小为1正是我所需要的。我有一个需要该值的路由守卫,但需要等待第一次发射。所以a BehaviorSubject并没有削减它,因为我不想要一个初始值(null因为我用它来表示一个状态
所以也不起作用

1
@ menehune23我还需要ReplaySubject来参加Angular resolve卫队课程。我的数据服务可以是异步的,也可以是同步的(如果已经检索到数据)。如果它是同步的,则在该resolve函数返回并由Angular内部订阅之前,将触发Subject.next()。BehaviourSubject可能会工作,但我必须明确调用complete()并还添加null初始值检查。什么工作是新ReplaySubject<DataType>(1)resolveSubject.asObservable().take(1).map(....)
Drenai

1
我使用的ReplaySubject缓冲区大小为1,但是由于某种原因,当我在调用ReplaySubject 之前获得带有Observable .asObservable()的Observable时,会null向订阅者发送一个值next()。我以为它不应该有一个不同于BehaviorSubject的初始值?
凯尔诉

2
我认为您可以提到的一个非常简单的示例是“聊天室”或游戏大厅场景,在该场景中您希望新的加入者看到最近的10条消息。
詹姆斯

14

我知道各种可观察类型的简要概述,不直观的命名

  • Subject -订阅后,订阅者将仅获得发布的值。
  • BehaviorSubject -新订阅者在订阅后立即获得最后发布的值或初始值。
  • ReplaySubject -新订阅者在订阅后立即获得所有先前发布的价值

1-n个发布的值?因此,如果有2个发布的值,则ReplaySubject将产生-1个发布的值?
郑宗盛

@JasonCheng不,它在订阅后会检索所有先前发布的值,更新答案:)
Ricky Boyce

11
  1. 主题:订阅时,总是获取订阅后推送的数据,即未收到先前推送的值
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);

在此示例中,这是将在控制台中打印的结果:

From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4

请注意,迟到的订阅是如何在推送到主题中的某些数据上丢失的。

  1. 重播主题:可以通过保留将要发送到新订阅的先前值缓冲区来提供帮助。

这是重播主题的用法示例,其中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);
});

这就是为我们提供控制台的功能:

From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
  1. 行为主题:与重播主题相似,但是将仅重新发射最后一个发出的值,或者如果先前未发出任何值,则默认为默认值:
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);
});

结果:

From 1st sub: Hey now!
From 1st sub: 5
From 2nd sub: 5

参考:https : //alligator.io/rxjs/subjects/


4

来自:兰德尔·库特尼克(Randall Koutnik)的书“使用RxJS构建反应性网站”。:

一个主题是一个对象,是一个涡轮增压观察到。从本质上讲,主题的行为很像常规的可观察对象,但是每个订阅都挂接到相同的源中。主题也是观察者,并且具有next,error和done方法可立即将数据发送给所有订户。由于主题是观察者,因此可以将它们直接传递到订阅调用中,并且来自原始可观察对象的所有事件都将通过主题发送给其订阅者。

我们可以使用ReplaySubject来跟踪历史记录。一个ReplaySubject记录最近的n个事件,并将它们发回给每个新订户。例如在聊天应用程序中。我们可以使用它来跟踪以前的聊天记录。

BehaviorSubject是的简化版本ReplaySubject。该ReplaySubject存储的事件的任意数,BehaviorSubject只记录最新事件的价值。每当BehaviorSubject记录一个新的订阅,它发出的最新值的用户,以及那些在通过任何新的价值。在BehaviorSubject与国家的独立单元,如配置选项打交道时非常有用。


1

最受支持的答案显然是错误的,声称:

“如果初始化一个ReplaySubject缓冲区大小为1的a,那么它实际上的行为就像一个BehaviorSubject


这不是完全正确的。有关两者之间的区别,请查看这篇出色的博客文章。例如,如果您预订了completed BehaviorSubject,则不会收到最后一个值,但是对于a,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));

在此处查看此代码示例该示例来自于该主题的另一篇很棒的博客文章


0
     // ***********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-新订阅者在订阅后立即获得最后发布的值或初始值。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.