rxjs中Observable和Subject的区别是什么?

152

我正在阅读这篇博客,了解Observable和Subject之间的区别,但是仍然不确定它们之间的差异。


3
Observible 是一个可以被操作并立即反映的数组/值。Subject 是一个 EventEmitter,它恰好是这样做的:发出事件。您随后可以基于该事件操作多个不同类型的观察者。 - Brandon Miller
2
请返回已翻译的文本。 - martin
10个回答

256

在流编程中,有两个主要接口: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')

17
完美的解释。+1 对于代码示例,尤其是使用观察者创建可观察对象的部分。 - Anddo
我认为,在Observable中,observable.filter和observable.map不是函数,Subject也是如此。 - Dinesh Gurjar
最好将Observable变量后缀命名为"$"以获得更好的编程实践。 - Isuru Madusanka

239
可观察对象是通过设计进行单播的,而主题是通过设计进行多播的。
如果您看下面的示例,每个订阅作为通过设计开发的可观察对象会接收到不同的值。
import {Observable} from 'rxjs';

let obs = new Observable<any>(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来解决这个问题。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());

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

55
用随机值作为示例,真是个好主意,远比只提到单播/组播更好。 - Mike de Klerk
1
Observable.create现在已被弃用,推荐使用new Observable()。但是,使用新语法时不能调用.next()方法。请问这种情况下的新语法是什么? - FiniteLooper
1
@FiniteLooper 你可以使用类似的方法:new Observable<string>((subscriber)=>{ subscriber.next('你好,世界'); }) - slimsim

75

可观察者

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

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

  3. 单向性:观察者不能将值分配给可观察对象(origin/master)。

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

  5. 如果它是我们想要在所有组件之间共享的服务,它不会具有最新结果,所有新订阅者仍将订阅同一可观察对象,并从头开始获取值。

  6. 单播意味着可以从可观察对象中发出值,而不是从任何其他组件中。

主题

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

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

  3. 双向性:观察者可以将值分配给可观察对象(origin/master)。

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

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


26

我觉得被接受的答案有点令人困惑!

Observer 并不是用于提供 Observable 数据源的接口,而是用于观察 Observable 数据源的接口... 从名称上更容易理解,对吧?

所以,这就是为什么:

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

创建一个可观察对象,该对象首先发出“first”,然后发出“second”的方法是将Observable.create(...)的参数作为订阅函数,它定义了在该Observable的直接Observer上会发生哪些Observer事件。

如果您想更深入地了解它,重要的是要理解,在订阅时,订阅函数不是Observer对象上直接调用的,而是由一个Subscription对象进行中介,该对象可以实施正确的可观察规则,例如在调用observer.complete()之后,即使您的订阅函数似乎会这样做,Observable也永远不会发出新值。

REF:http://reactivex.io/rxjs/manual/overview.html#creating-observables

Subject既是Observable又是Observer,再次强调,看起来就像Observer接口是'feed'事件到Subject的方法。但是如果您意识到Subject有类似于等效于订阅函数的东西(即在创建后定义观察它的事物将发生哪些事件的地方)坐在对象上,那么更容易理解命名。因此,您在Subject上调用Observer方法来定义观察它的事物将会发生哪些Observer事件!(同样,涉及中间对象,以确保您只能执行合法的一系列操作。)

REF:http://reactivex.io/rxjs/manual/overview.html#subject


4
阅读完被采纳的答案后,我也感到困惑,不确定是只有我还是其他人也对它不满意。感谢您发表您的看法。 - Imran Latif

21
请参阅RxJS文档(那里有更多信息和示例):http://reactivex.io/rxjs/manual/overview.html#subject。什么是Subject?RxJS Subject是一种特殊类型的Observable,允许将值多播给多个Observers。虽然普通的Observables是单播的(每个订阅的Observer拥有一个独立的Observable执行),但Subjects是多播的。Subject就像Observable一样,但可以向多个观察者进行多播。Subjects类似于EventEmitters:它们维护着许多监听器的注册表。代码上,Subject扩展了Observablehttps://github.com/ReactiveX/rxjs/blob/master/src/internal/Subject.ts#L22
/**
 * @class Subject<T>
 */
export class Subject<T> extends Observable<T> implements SubscriptionLike {
//...
}

16

简而言之,

主题: 你可以向它发送消息并从中接收消息。

可观察对象: 你只能从中接收消息。

换句话说,在主题中,你可以随时随地使用它来订阅和广播给其他订阅者。

然而,在可观察对象中,你只能订阅它(在初始化后不能使用它广播数据)。 唯一可以从可观察对象广播数据的地方是在其构造函数中。


1
好观点!这是我刚刚意识到的一个重要区别。 - themefield

15

想象一下,如果你的应用程序中有一个数据流,例如WebSocket连接。你需要找到一种处理这些数据的方式,有几种解决方案:

1. 普通Ajax请求: 这种解决方案并不可行,因为它不能处理推送数据。它更像是拉取而不是推送。

2. Promise: 也不太好,因为你必须触发它们,并且它们只能检索一次。也更像是拉取而不是推送。

因此,在以前,为了检索这些数据,我们使用长轮询。这就是我们设置一个间隔函数,例如每1分钟检索一次该数据流的位置。虽然它起作用,但实际上会拖累资源,如CPU和内存。

但现在有了第三个选项:

3. Observable:你可以订阅并让数据流不停地进入,直到完成函数被调用。

很酷,对吧?但接着还有另一个问题:如果你只想在应用程序中的某个地方观察传入的数据一次,但想在数据到达时同时在应用程序中使用该数据,那么就可以使用Subject了。你在想要在整个应用程序中使用的位置放置subject.subscribe()。当数据到达时,任何使用subject.subscribe()的地方都将同时处理它们。但观察者必须订阅该subject作为其参数,就像这样:

观察者通过subject进行订阅(observer.subscribe(subject))。

一个例子是当你想要构建通知提醒时的应用程序。

由于每个订阅者可能会收到不同的输入数据,所以您不能多次订阅相同的observable。但是通过subject,所有通过subject订阅()的人将检索相同的数据。

另一个类比是杂志订阅。每个订阅者都会收到带有他们姓名的杂志。因此,不同的订阅=不同的接收者名称。(正常的Observable)但是当您分享给朋友时,所有朋友都只会收到带有您姓名的相同杂志。(带有Subject的普通Observable)

这位先生用代码示例很好地解释了它。您可以在此处查看。

希望这个答案有所帮助。


1
我发现使用日常生活中的普通事物类比更容易理解,而不是术语对术语。 - Jeb50

13

Observable 能够通知单个观察者,而 Subject 可以通知多个观察者。


对于每个订阅 Observable 的输出都是不同的,但如果您希望在不同的观察者中期望相同的输出,则建议使用 Subject! - jaibalaji

7
从另一个角度来看,值得注意的是订阅Observable会重新执行Observable函数。如果数据源是服务,这可能会导致性能问题。 如果您希望多个订阅者获得相同的值,则可能需要使用Subject。 为此,请确保在Subject订阅数据源之前设置了您的订阅。否则,您的进程将被卡住。
更多详细信息请参见:https://javascript.tutorialhorizon.com/2017/03/23/rxjs-subject-vs-observable/

5

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接