我一直想了解这3个:

主题 BehaviorSubject ReplaySubject

我想使用它们,知道什么时候和为什么,使用它们的好处是什么,虽然我已经阅读了文档,观看教程和搜索谷歌,但我没有搞清楚这一点。

那么它们的目的是什么呢?如果是真实的情况,甚至不需要编写代码,那就最好不过了。

我更喜欢一个干净的解释,而不仅仅是“a+b => c您订阅了....”

谢谢你!


这实际上归结为行为和语义。与一个

Subject - a subscriber will only get published values that were emitted after the subscription. Ask yourself, is that what you want? Does the subscriber need to know anything about previous values? If not, then you can use this, otherwise choose one of the others. For example, with component-to-component communication. Say you have a component that publishes events for other components on a button click. You can use a service with a subject to communicate. BehaviorSubject - the last value is cached. A subscriber will get the latest value upon initial subscription. The semantics for this subject is to represent a value that changes over time. For example a logged in user. The initial user might be an anonymous user. But once a user logs in, then the new value is the authenticated user state. The BehaviorSubject is initialized with an initial value. This is sometimes important to coding preference. Say for instance you initialize it with a null. Then in your subscription, you need to do a null check. Maybe OK, or maybe annoying. ReplaySubject - it can cache up to a specified number of emissions. Any subscribers will get all the cached values upon subscription. When would you need this behavior? Honestly, I have not had any need for such behavior, except for the following case: If you initialize a ReplaySubject with a buffer size of 1, then it actually behaves just like a BehaviorSubject. The last value is always cached, so it acts like a value changing over time. With this, there is no need for a null check like in the case of the BehaviorSubject initialized with a null. In this instance, no value is ever emitted to the subscriber until the first publishing.

因此,这实际上取决于您所期望的行为(至于使用哪一种)。大多数情况下,您可能想要使用BehaviorSubject,因为您真正想要表示的是“随时间变化的值”语义。但我个人不认为用1初始化ReplaySubject的替换有任何错误。

当您真正需要缓存行为时,您要避免使用普通Subject。例如,您正在编写路由保护或解析。您从该保护中获取一些数据,并将其设置在服务Subject中。然后在路由组件中订阅服务主题,以尝试获取在保护中发出的值。哦。价值在哪里?它已经被释放了,DUH。使用“缓存”主题!

参见:

什么是RxJS Subject和使用它们的好处?


不同可观察类型的一个方便的总结,非直观的命名我知道lol。

Subject——订阅者只会在订阅之后获得已发布的值。 BehaviorSubject——新订阅者在订阅后立即获得最后发布的值或初始值。 ReplaySubject -新订阅者在订阅时立即获得所有以前发布的值


摘自:Randall Koutnik的书《用RxJS构建响应式网站》。”:

一个主题是一个对象,它是一个涡轮增压的可观察对象。就其核心而言,Subject的行为很像常规的可观察对象,但每个订阅都连接到相同的源。主体也是观察者,它们有next、error和done方法,可以一次性将数据发送给所有订阅者。因为主题是观察者,所以它们可以直接传递到订阅调用中,来自原始可观察对象的所有事件都将通过主题发送给它的订阅者。

我们可以用ReplaySubject追踪历史。ReplaySubject记录最近n个事件,并将它们回放给每个新订阅者。例如在聊天应用程序中。我们可以用它来追踪之前的聊天记录。

行为主体是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")

Subject——订阅者只会在订阅之后获得已发布的值。 BehaviorSubject——新订阅者在订阅后立即获得最后发布的值或初始值。


Subject:在订阅时,它总是获得在订阅后推送的数据,即之前推送的值没有收到。

const mySubject = new Rx.Subject(); mySubject.next (1); const subscription1 = mySubject订阅(x => { console.log('从订阅1:',x); }); mySubject.next (2); const subscription2 = mySubject。订阅(x => { console.log('从订阅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 " > < /脚本>

在这个例子中,结果会打印在控制台中:

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

请注意,延迟到达的订阅如何错过了推入主题的一些数据。

重放主题:可以通过保留将被发送到新订阅的先前值的缓冲区来帮助。

这里有一个重放主题的使用示例,其中保留了2个先前值的缓冲区,并在新的订阅上发出:

const mySubject = new Rx.ReplaySubject(2); mySubject.next (1); mySubject.next (2); mySubject.next (3); mySubject.next (4); mySubject。订阅(x => { console.log('From 1st sub:', x); }); mySubject.next (5); mySubject。订阅(x => { console.log('From 2nd sub:', x); }); < script src = " https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.min.js " > < /脚本>

这是我们在控制台得到的结果:

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(“嘿!”); mySubject。订阅(x => { console.log('From 1st sub:', x); }); mySubject.next (5); mySubject。订阅(x => { console.log('From 2nd sub:', x); }); < script src = " https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.12/Rx.min.js " > < /脚本>

结果是:

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

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


被点赞最多的回答显然是错误的:

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


这并不完全正确;看看这篇关于两者区别的博客文章。例如,如果你订阅了一个完整的BehaviorSubject,你将不会收到最后一个值,但对于ReplaySubject(1),你将收到最后一个值。

这是一个不容忽视的重要区别:

const behavior = new behaviour subject (null); const replay = new ReplaySubject(1); behavior.skip(1)。订阅(v => console.log('BehaviorSubject:', v)); 重播。订阅(v => console.log('ReplaySubject:', v)); behavior.next (1); behavior.next (2); behavior.complete (); 的行为。订阅(v => console.log('晚期B订阅者:',v)); replay.next (1); replay.next (2); replay.complete (); 重播。订阅(v => console.log('后期R订阅者:',v));

查看这里的代码示例,它来自另一篇关于该主题的很棒的博客文章。


正如在一些帖子中提到的,公认的答案是错误的,因为BehaviorSubject != ReplaySubject(1),这不仅仅是编码风格的偏好。

评论中经常提到“守卫”,这也是我发现重放主题用例最多的地方。更具体地说,如果你有一个类似于取(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的值getter来获取当前值。当您在某些情况下只需要当前值时,这是非常有用的。例如,当用户单击某个内容,而您只需要该值一次时。在这种情况下,你不需要订阅然后突然取消订阅。唯一需要的是:

BehaviorSubject bSubject = new BehaviorSubject<IBasket>(basket);

getCurrentBasketValue() {
  return this.bSubject.value;
}