Angular中的Subject vs BehaviorSubject vs ReplaySubject

时间:2017-03-30 13:05:31

标签: javascript angular rxjs reactive-programming angular2-observables

我一直在寻找那些3:

主题行为主题重播主题。我想使用它们,知道何时以及为什么,使用它们有什么好处,虽然我已经阅读了文档,观看了教程并搜索了谷歌我没有对此有任何意义。

那么他们的目的是什么?一个真实的案例将是最受欢迎的,它甚至不必编码。

我更喜欢干净的解释而不只是" a + b => c您订阅了...."

谢谢

7 个答案:

答案 0 :(得分:169)

这真的归结为行为和语义。用

  • Subject - 订阅者只会获得订阅后发布的已发布值。问问自己,这就是你想要的吗?订户是否需要了解以前的值?如果没有,那么你可以使用它,否则选择其中一个。例如,使用组件到组件的通信。假设您有一个组件,可以通过单击按钮发布其他组件的事件。您可以使用与主题进行通信的服务。

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

    BehaviorSubject初始化为初始值。这有时对编码偏好很重要。比如说你用null初始化它。然后在您的订阅中,您需要进行空检查。也许还好,或者说很烦人。

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

    如果您初始化缓冲区大小为ReplaySubject的{​​{1}},那么它实际上的行为就像1一样。最后一个值始终被缓存,因此它的作用就像一个随时间变化的值。有了这个,就不需要BehaviorSubject检查,就像使用null初始化的BehaviorSubject一样,因为在第一次发布之前,没有任何值发送给订阅者。 / p>

所以它真正降低了你期望的行为,至于使用哪一种行为。大多数情况下,您可能希望使用null,因为您真正想要表达的是"随时间变化的值"语义。但我个人认为用BehaviorSubject初始化的ReplaySubject的替换没有任何问题。

当您真正需要的是一些缓存行为时,您希望避免使用vanilla 1。例如,您正在编写路由警卫或解决方案。您在该防护中获取一些数据并将其设置为服务Subject。然后在路由组件中,您订阅服务主题以尝试获取在防护中发出的值。哎呀。价值在哪里?它已经被释放了,DUH。使用"缓存"主题!

另见:

答案 1 :(得分:3)

关于各种可观察类型的方便总结,我不知道这种不直观的命名。

  • Subject-订阅后,订阅者只会在其上获得发布的值。
  • BehaviorSubject-新订阅者在订阅后立即获得上次发布的值或初始值。
  • ReplaySubject-新订阅者在订阅后会立即获得最后1-n个发布的值(仅在先前发出时)。

答案 2 :(得分:3)

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

答案 3 :(得分:2)

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

”“如果您初始化缓冲区大小为1的ReplaySubject,那么它实际上的行为就像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的此代码示例another great blog post

答案 4 :(得分:0)

来自:Randall Koutnik的书“使用RxJS构建反应性网站”。

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

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

BehaviorSubject ReplaySubject 的简化版本。 ReplaySubject 存储了任意数量的事件, BehaviorSubject 仅记录了最新事件的值。每当 BehaviorSubject 记录新订阅时,它会向订阅者发出最新值以及传入的任何新值。 BehaviorSubject 在处理单个单元时非常有用状态,例如配置选项。

答案 5 :(得分: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-新订阅者在订阅后立即获得上次发布的值或初始值。

答案 6 :(得分:0)

正如一些帖子中提到的,自 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