【问题标题】:What is the difference between Observable and a Subject in rxjs?rxjs 中的 Observable 和 Subject 有什么区别?
【发布时间】:2018-05-12 06:51:11
【问题描述】:

我正在阅读 blog 并阅读有关 Observables 的内容,但无法弄清楚 Observable 和 Subject 之间的区别。

【问题讨论】:

标签: javascript angular rxjs reactive-programming


【解决方案1】:

在流式编程中有两个主要接口:ObservableObserver

Observable是给消费者的,可以转化订阅:

observable.map(x => ...).filter(x => ...).subscribe(x => ...)

Observer 是用于提供可观察源的接口:

observer.next(newItem)

我们可以用Observer创建新的Observable

var observable = Observable.create(observer => { 
    observer.next('first'); 
    observer.next('second'); 
    ... 
});
observable.map(x => ...).filter(x => ...).subscribe(x => ...)

或者,我们可以使用实现 ObservableObserver 接口的 Subject

var source = new Subject();
source.map(x => ...).filter(x => ...).subscribe(x => ...)
source.next('first')
source.next('second')

【讨论】:

  • 完美的解释。 +1 对于代码示例,专门使用观察者创建可观察对象。
  • 我认为,在 Observable 中 observable.filter 和 observable.map 不是函数,与 Subject 相同。
【解决方案2】:

Observables 被设计为单播,Subjects 被设计为多播。

如果您查看下面的示例 - 每个订阅都会收到不同的值,作为按设计开发为单播的 observables。

import {Observable} from 'rxjs';

let obs = Observable.create(observer=>{
   observer.next(Math.random());
})

obs.subscribe(res=>{
  console.log('subscription a :', res); //subscription a :0.2859800202682865
});

obs.subscribe(res=>{
  console.log('subscription b :', res); //subscription b :0.694302021731573
});

如果您期望两个订阅的值相同,这可能会很奇怪。

我们可以使用主题来解决这个问题。 Subjects 类似于事件发射器,它不会为每个订阅调用。请考虑以下示例。

import {Subject} from 'rxjs';

let obs = new Subject();

obs.subscribe(res=>{
  console.log('subscription a :', res); // subscription a : 0.91767565496093
});

obs.subscribe(res=>{
  console.log('subscription b :', res);// subscription b : 0.91767565496093
});

obs.next(Math.random());

两个订阅都得到相同的输出值!

【讨论】:

  • 用一个随机值来演示真是个好主意,比仅仅参考单播/多播要好得多。
【解决方案3】:

可观察的

  1. 它们很冷:当它们至少有一个观察者时,代码才会被执行。

  2. 创建数据副本:Observable 为每个观察者创建数据副本。

  3. 单向:Observer 无法为 observable(origin/master) 赋值。

  4. 代码将为每个观察者运行。如果它是一个 HTTP 调用,它会被每个观察者调用。

  5. 如果它是我们想要在所有组件之间共享的服务,它不会有最新结果所有新订阅者仍将订阅相同的 observable 并从头开始获得价值

  6. 单播方式可以从可观察对象而不是任何其他组件发出值。

主题

  1. 它们很热:即使没有观察者,也会执行代码并广播值。

  2. 共享数据:所有观察者共享相同的数据。

  3. 双向:Observer 可以为 observable(origin/master) 赋值。

  4. 如果使用的是 using 主题,那么您会错过在创建观察者之前广播的所有值。于是重播主题来了

  5. 多播,可以将值投射到多个订阅者,并且可以充当订阅者和发送者

【讨论】:

    【解决方案4】:

    查看 rxjs 文档(更多信息和示例在那里): http://reactivex.io/rxjs/manual/overview.html#subject

    什么是主题? RxJS Subject 是一种特殊类型的 Observable,它允许将值多播到多个 Observer。虽然普通的 Observable 是单播的(每个订阅的 Observer 都拥有 Observable 的独立执行),但 Subjects 是多播的。

    Subject 类似于 Observable,但可以多播到多个 Observer。 Subjects 就像 EventEmitters:它们维护着许多监听器的注册表。

    和代码,Subject 扩展 Observable: https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subject.ts#L22

    /**
     * @class Subject<T>
     */
    export class Subject<T> extends Observable<T> implements SubscriptionLike {
    //...
    }
    

    【讨论】:

      【解决方案5】:

      我发现接受的答案有点令人困惑!

      Observer 不是提供Observable源的接口,它是观察 可观察来源...从名称上看更有意义,对吧?

      所以,原因是:

      var observable = Observable.create(observer => { 
          observer.next('first'); 
          observer.next('second'); 
          ... 
      });
      

      工作 - 创建一个发出“第一”然后“第二”的可观察 - 是 Observable.create(...) 的参数是一个 订阅函数,它基本上定义了哪个 观察者事件将发生在该Observable的直接Observer上。

      如果你想再深入一点,重要的是要了解订阅函数不是在你订阅时直接在Observer对象上调用,而是由 Subscription 对象调解,该对象可以强制执行正确的可观察规则,例如在调用observer.complete() 之后,Observable 将永远不会发出新值,即使您的 subscribe 函数看起来好像会发出新值。

      参考号:http://reactivex.io/rxjs/manual/overview.html#creating-observables

      Subject 既是 Observable 又是 Observer 并且再次看起来 就像 >观察者界面是向主题“提供”事件的方式。但是,如果您意识到 Subject 有点像 Observable 并具有等价的 subscribe 功能(即您定义将发生的事件的位置),则更容易理解命名观察它的东西)就坐在对象上,即使在它被创建之后也是如此。因此,您调用 Subject 上的 Observer 方法来定义观察它的事物会发生哪些 Observer 事件! ?(同样,其中涉及到中间对象,以确保您只能执行合法的事情序列。)

      参考号:http://reactivex.io/rxjs/manual/overview.html#subject

      【讨论】:

      • 我在阅读接受的答案后也很困惑,想知道是我还是其他人也不满意。感谢您发表您的想法。
      【解决方案6】:

      Observable 只能通知一个观察者,而 Subject 可以通知多个观察者。

      【讨论】:

      • 对于每个订阅的可观察输出是不同的,但如果您希望在不同的观察者中获得相同的输出,建议使用主题!
      【解决方案7】:

      想象一下,如果您有一个数据流进入您的应用程序,就像在 websocket 连接中一样。你想要一种方法来处理它。有几个解决办法:

      1.普通的ajax请求: 这个解决方案是不可行的,因为它是 不适用于处理推送数据。它更像是一种拉力,而不是一种 推。

      2。承诺: 也不好,因为你必须触发它们并且 他们只能检索一次。更多的是拉而不是推动。

      所以为了检索这些数据,在过去,我们进行了一次长轮询。例如,我们在其中设置了一个间隔函数以每 1 分钟检索一次该数据流。虽然它可以工作,但它实际上会增加 CPU 和内存等资源的负担。

      但是现在有了第 3 个选项,

      3。 Observable:您可以订阅并让数据流来 一直不停,直到函数完成被调用。

      很酷吧?但是还有另一个问题。如果您只想在应用程序的某个地方观察传入数据一次怎么办。但是当数据到达时,您希望在应用程序周围同时使用这些数据。那就是您使用主题的时间和地点。 您将 subject.subscribe() 放置在您想要在整个应用程序中使用的位置。当数据到达时,有 subject.subscribe() 的地方会同时处理它们。但是观察者必须像这样订阅主题作为它的参数。

      observer.subscribe(主题)。

      示例应用程序是您想要构建通知警报时。

      您不能对同一个 observable 进行多个订阅,因为每个订阅者可能会收到不同的输入数据。但是对于主题,所有通过主题的 subscribe() 都将检索相同的数据。

      另一个类比是通过杂志订阅。每个订阅者都会收到带有他们名字的杂志。所以,不同的订阅 = 不同的接收者名称。(普通 Observable) 但是当你和你的朋友分享时,你所有的朋友都会收到同一份杂志,上面只有你的名字。(Normal Observable with Subject)

      这家伙用代码示例很好地解释了它。你可以在https://javascript.tutorialhorizon.com/2017/03/23/rxjs-subject-vs-observable/查看它

      希望这个答案有所帮助。

      【讨论】:

        【解决方案8】:

        从另一个角度来看,值得注意的是,订阅Observable 会重新执行Observable 函数。例如,如果数据源是服务,这可能会导致性能问题。

        如果您希望多个订阅者获得相同的值,您可能需要一个主题。 为此,请确保在主题订阅数据源之前设置您的订阅。否则你的进程会卡住。

        更多详情:https://javascript.tutorialhorizon.com/2017/03/23/rxjs-subject-vs-observable/

        【讨论】:

          【解决方案9】:

          简单地说,

          主题:您可以向它发送并从中接收

          可观察:您可以从中接收

          换句话说, 在主题中,您可以订阅并且您可以使用它在任何时间、任何地点向其他订阅者广播代码。

          同时, 在 observable 中,您可以订阅 (初始化后不能使用它来广播数据)。 唯一可以从 observable 广播数据的地方是它的构造函数。

          【讨论】:

            【解决方案10】:

            可观察: 只有 Observable 知道事件是如何以及何时在 observable 上触发的。即 next() 方法只能在实例化的构造函数中调用。此外,每次订阅时,都会创建一个单独的观察者,并仅使用构造函数内部的特定观察者调用next() 方法,在以下示例中,subscriber 本身就是观察者,并且在实例化的构造函数被执行时被订阅。 例如:

            import { Observable } from 'rxjs';
            
            const observable = new Observable(subscriber => {
              subscriber.next(1);
              subscriber.next(2);
              setTimeout(() => {
                subscriber.next(3);        
              }, 1000);
            });
            

            主题: 这里next() 方法可以在构造函数之外的任何地方被主体使用。此外,在订阅之前调用next() 方法时,将错过特定事件。因此next() 方法只能在订阅后调用。 例如:

            import { Subject } from 'rxjs';
             
            const subject = new Subject<number>();
             
            
            subject.next(1); // this is missed
            subject.subscribe({
              next: (v) => console.log(`observerA: ${v}`)
            });
            subject.subscribe({
              next: (v) => console.log(`observerB: ${v}`)
            });     
            subject.next(2);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-07-13
              • 2019-02-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-30
              • 2016-09-18
              • 2017-03-22
              相关资源
              最近更新 更多