RxJS:基础知识
通过RxJS学习Angular中函数式响应式编程的基础知识。
为什么?
你可能会问:
Angular和函数式响应式编程有什么关系?
实际上,很多。
在Angular中,所有异步的东西都是可观测的
,这是反应性编程的核心。
如果你习惯使用承诺,那么可观察对象是非常简单的。一个简单的解释是,可观察对象就像承诺一样,可以在一段时间内发出多个值——或者称为流。当然,这有点简化,但这是我们的起点。
Meetup演讲
本文主要基于我在2018年2月落基山Angular聚会上的一次meetup演讲。看看YouTube上的视频:
演示文件
在这篇文章中,我将引用一些演示文件,这些文件是我为演示准备的。你可以透过以下途径下载:
演示文件使用的是webpack-dev-server。所有演示都包含在其中<年代trong>src /演示目录中,您可以切换哪个演示程序正在运行<年代trong>src / index.tswebpack的入口。只需取消你想要执行的演示的注释,然后加载http://localhost:8080在您的浏览器中。对演示文件或索引文件和浏览器的任何更改都将随更新的更改而重新加载。当然,确保一次只运行一个演示(为了清晰起见)。
RxJS是什么?
RxJS是ReactiveX库的JavaScript实现。ReativeX让Angular开发者能够构建异步的、模块化的、基于事件的应用。它允许我们开发这些应用程序反应性地。一开始这有点让人困惑,但是当你深入到RxJS时,你就会开始了解这个概念有多强大,以及RxJS提供的许许多多操作符有多强大。
回到数据流的概念。在承诺使一个异步事件或值被实现或拒绝,RxJS启用多个要观察的异步事件或值。
这是新的吗?
是的,没有。是的,这对JavaScript开发人员来说可能是新的。但是,这种概念或设计模式并不新鲜。
如果你指的是著名的四人组设计模式手册你会发现观察者设计模式:
简要解释这个概念:
- 一个
主题
类包含一个观察者的集合。 - 每一个
ConcreteObserver
扩展一个抽象观察者
类,实现update ()
方法。 - 观察者将自己注册到
主题
,也可以自行注销。 - 的
主题
通知所有观察者一个事件或值。
可见是什么?
简单地说,可观察对象实现了观察者设计模式。一个(或多个)观察者收到一个通知
这是由可观察对象发出的。
在RxJS中,这是由恰当命名的可观测的
类,它包含静态方法和实例方法来创建类的实例,过滤和转换observable发出的值,等等。此外,可观测的
方法是围绕Array.prototype
JavaScript开发人员熟悉的方法,包括:filter ()
,map ()
,reduce ()
等。
从Promiseland
为了了解我们从哪里来,我们要去哪里,让我们快速回顾一下承诺
。简单地取消进口“。/演示/承诺”;
线<年代trong>src / index.ts文件。这里是内容<年代trong>src /演示/ promise.ts文件:
/*创建一个新的promise。* /常量p<年代p一个class="token operator">=新承诺(解决= >{setTimeout(()= >{解决(“从Promiseland你好!”);},1000);});/*发送的单个日志值。* /p<年代p一个class="token punctuation">。然后(价值= >控制台<年代p一个class="token punctuation">。日志(价值<年代p一个class="token punctuation">));
快速回顾:
- 的
承诺
类被嵌入到我们今天的浏览器中。因此,我们不需要从库中导入任何东西。 - 我们创建一个新的
承诺
,提供了executor函数,该函数接受两个参数a解决
函数和一个拒绝
函数。在这个演示中,我们只使用解决
函数。 - 异步事件将导致承诺被履行或被拒绝。
- 在本例中,我们只是使用
setTimeout ()
函数来模拟异步事件。一秒钟后,发出字符串值“Hello from Promiseland”。
对Observableland
既然我们对单个异步值有承诺,那么可观察对象就提供了随时间发出值流的能力。让我们看一个简单的例子<年代trong>src /演示/ observable.ts:
进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;/*创建一个新的可观察对象,提供订阅函数。* /常量可观测的<年代p一个class="token operator">:可观测的<年代p一个class="token operator"><字符串<年代p一个class="token operator">>=新可观测的(观察者= >{常量时间间隔<年代p一个class="token operator">=setInterval(()= >{观察者<年代p一个class="token punctuation">。下一个(“从Observableland你好!”);},1000);/ /拆卸返回()= >{clearInterval(时间间隔<年代p一个class="token punctuation">);};});/*订阅通知。* /可观测的<年代p一个class="token punctuation">。订阅(价值= >控制台<年代p一个class="token punctuation">。日志(价值<年代p一个class="token punctuation">));
- 首先,我们需要导入
可观测的
类,作为可观察对象还不是ECMAScript (JavaScript)语言的一部分。 - 然后,我们创建一个新的
可观测的
,提供订阅函数,该函数在观察者
订阅可观察对象。 - 我们可以用
next ()
方法在一个观察者
对象向观察者发送值。的观察者
接口需要由对象实现三个方法:next ()
,抓住()
和完成()
。 - 在本例中,我们使用
setInterval ()
函数来模拟将随时间发出值的异步事件。每一秒我们都在发出字符串“Hello from Observableland!” - 然后,我们返回一个teardown函数,当所有观察者都取消订阅了observable时,这个函数就会被调用。
- 最后,我们调用
订阅()
方法,提供每次调用的函数next ()
值被发送给所有观察者。
什么是通知
吗?
的通知
类表示或包装了可观察流正在发出的事件或值。通知被通过一个可观测的观察员,并包括元数据类型的事件或价值:如果事件或流值是下一个值,或者如果该值是一个例外,如果流已经完成并将不再发出任何未来值。
什么是观察者
吗?
一个观察者
收到一个通知
从一个可观测的
由a管理订阅
。一个观察者将反应到下一个,错误和完成通知。
在Angular中,我们通常是框架中的可观察对象发出的异步事件或值的消费者或观察者。Angular中一些常见的用例是HTTP请求和路由参数。
什么是订阅
吗?
一个订阅
是可观察对象和观察者之间的联系。调用时返回订阅对象订阅()
方法在一个可观测的
。订阅对象有两个重要的方法:退订()
和add ()
。的退订()
方法将从observable中的观察者集合中移除一个观察者,以及任何子订阅对象。属性,可以向现有订阅添加子订阅add ()
方法。
是否包含在ECMAScript中?
还没有。
可观察对象目前是a第一阶段的建议由Ecma国际内部的TC39组为ECMAScript, JavaScript实现。在“不久”的将来,我们将看到可观察对象嵌入到我们的浏览器中。
在此之前,我们需要使用像RxJS这样的库。而且,在撰写本文时,observable建议还没有包括RxJS中包含的许多响应式编程操作符。因此,在可预见的将来,我们可能会使用RxJS。
什么是主题
吗?
一个主题
是一个专门的可观察对象,它允许我们将值多播给许多观察者,并且有几个实现的行为。因为主题
类继承了可观测的
它继承了observable的所有相同的方法和属性。所以,主体既是可观察对象也是观察者。这是我们需要学习的一个重要而有力的概念。
让我们来看看<年代trong>src /演示/ subject.ts:
进口{主题<年代p一个class="token punctuation">}从“rxjs /主题”;/*创建一个Subject的实例。* /常量年代<年代p一个class="token operator">=新主题<数量<年代p一个class="token operator">>();/*订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(1:之前的,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(“之前完成1”));年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(2:之前的,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(2之前完成的));/*释放一些值。* /年代<年代p一个class="token punctuation">。下一个(1);年代<年代p一个class="token punctuation">。下一个(2);年代<年代p一个class="token punctuation">。下一个(3.);/*晚订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(”后,“,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(完成后的));/*逾期订阅将收到通知。* /年代<年代p一个class="token punctuation">。下一个(4);年代<年代p一个class="token punctuation">。完整的();
在这个例子中:
- 我们进口
主题
类,然后创建一个新实例。 - 我使用的是TypeScript,所以我已经将这个主题的泛型类型指定为type
数量
。这意味着我可以期望可观察流发出的值是数字。 - 接下来,我们创建两个新订阅。我们为这三个通知都提供了一个回调函数:下一个,抓和完成,以这个顺序作为参数
订阅()
方法。 - 我只是将通知的结果注销到控制台。
- 然后,使用
next ()
方法:1、2、3。 - 然后在发出前三个值之后添加第三个订阅。
- 然后,使用
next ()
方法:4。 - 最后,我们
完成()
可观察流,通知观察者流将不再发出未来的值。
如果你检查你的控制台,你应该看到:
1:1之前2:1之前2:2之前1:3之前2:3之前1:4之前2:4之后:4完成之前1完成之前2完成之后
注意,订阅可观察对象的顺序很重要,因为第三个订阅只接收到第四个值(4)主题
我们是否可以调用一个可观察对象next ()
方法向观察者发送附加值。
什么是AsyncSubject
吗?
的AsyncSubject
类继承了主题
并继承了a的所有方法和属性主题
。还有,别忘了主题
扩展Obserable
。
的AsyncSubject
三个中有一个不同吗主题
由RxJS库实现的行为。我们来看看所有这些。
这里是一个例子,位于<年代trong>src /演示/ async-subject.ts:
进口{AsyncSubject<年代p一个class="token punctuation">}从“rxjs / AsyncSubject”;/*创建一个AsyncSubject实例。* /常量年代<年代p一个class="token operator">=新AsyncSubject<数量<年代p一个class="token operator">>();/*订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(“:”,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(之前完成的));/*释放一些值。* /年代<年代p一个class="token punctuation">。下一个(1);年代<年代p一个class="token punctuation">。下一个(2);年代<年代p一个class="token punctuation">。下一个(3.);/*晚订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(”后,“,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(完成后的));/*完成可观察流。* ///我们必须完成,因此值被发送到订阅年代<年代p一个class="token punctuation">。完整的();
这个示例与前面的示例非常相似主题
类。最主要的区别是AsyncSubject
将只接收最后发出的值,并且只在完成时接收。
在你的控制台你应该看到:
before: 3 after: 3 complete before complete after
注意,订阅可观察流的顺序是不相关的。您还可以通过注释掉完成()
方法调用。当你这样做的时候,你应该在控制台中看不到任何东西,因为观察者永远不会收到通知。
什么是BehaviorSubject
吗?
当一个观察者订阅aBehaviorSubject
它接收可观察对象最近发出的通知,然后继续接收以后的通知,直到流完成。为了实现这一点,我们必须在创建新对象时提供一个种子值(或默认值)BehaviorSubject
。
让我们看一个例子<年代trong>src /演示/ behavior-subject.ts文件:
进口{BehaviorSubject<年代p一个class="token punctuation">}从“rxjs / BehaviorSubject”;/*创建一个BehaviorSubject的实例。* /常量年代<年代p一个class="token operator">=新BehaviorSubject()<数量<年代p一个class="token operator">>0;/*订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(“:”,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(之前完成的));/*释放一些值。* /年代<年代p一个class="token punctuation">。下一个(1);年代<年代p一个class="token punctuation">。下一个(2);年代<年代p一个class="token punctuation">。下一个(3.);/ / s.complete ();/*晚订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(”后,“,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(完成后的));
快速回顾:
- 首先,我们导入
BehaviorSubject
类,然后创建一个提供种子值的新实例。 - 接下来,我们订阅可观察流。
- 然后,我们发出三个值:1、2、3。
- 然后我们再次订阅(在发出三个值之后)。
下面是注销到控制台的内容:
before: 0 before: 1 before: 2 before: 3 after: 3
有几件事需要注意:
- 请注意,在我们调用之前创建的订阅
next ()
接收的种子值为0。 - 注意,在发出第三个值(3)之后创建的订阅也收到了最新的通知。
- 如果取消对调用的行进行注释
完成()
,请注意,第二个订阅从未收到a下一个通知,但仍然接收完成通知。
什么是ReplaySubject
吗?
顾名思义,aReplaySubject
向任何观察者发送源可观察对象发出的所有通知,不管观察者何时订阅。
让我们看一个例子<年代trong>src /演示/ replay-subject.ts:
进口{ReplaySubject<年代p一个class="token punctuation">}从“rxjs / ReplaySubject”;/*创建ReplaySubject的实例。* /常量年代<年代p一个class="token operator">=新ReplaySubject<数量<年代p一个class="token operator">>();/*订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(“:”,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(之前完成的));/*释放一些值。* /年代<年代p一个class="token punctuation">。下一个(1);年代<年代p一个class="token punctuation">。下一个(2);年代<年代p一个class="token punctuation">。下一个(3.);/*晚订阅主题。* /年代<年代p一个class="token punctuation">。订阅(下一个= >控制台<年代p一个class="token punctuation">。日志(”后,“,下一个<年代p一个class="token punctuation">),错误= >控制台<年代p一个class="token punctuation">。警告(错误<年代p一个class="token punctuation">),()= >控制台<年代p一个class="token punctuation">。日志(完成后的));/*完成可观察流。* /年代<年代p一个class="token punctuation">。完整的();
上面的代码与前面的示例非常相似。注意,我们发出完成两个订阅后的通知。
下面是输出到控制台的内容:
before: 1 before: 2 before: 3 after: 1 after: 2 after: 3 complete before complete after
在本例中,无论何时创建订阅,两个订阅都会接收所有通知。
单播和多播
一个可观测的
是单播的,因为每个订阅的观察者都拥有一个独立的可观察对象的执行。
让我们看一个示例,该示例将向我们展示对于每个订阅,我们将执行订阅
函数提供给可观测的
的构造函数()
函数。
演示程序位于<年代trong>src /演示/ obserable-unicast.ts文件:
进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;/*创建一个新的可观察对象,提供订阅函数。* /让我<年代p一个class="token operator">=0;常量可观测的<年代p一个class="token operator">:可观测的<年代p一个class="token operator"><数量<年代p一个class="token operator">>=新可观测的(观察者= >{控制台<年代p一个class="token punctuation">。日志(' % cNew创建订阅的,背景:# 222;颜色:# bada55”);我<年代p一个class="token operator">++;常量时间间隔<年代p一个class="token operator">=setInterval(()= >{观察者<年代p一个class="token punctuation">。下一个(我<年代p一个class="token punctuation">);},1000);返回()= >{clearInterval(时间间隔<年代p一个class="token punctuation">);};});/*每个订阅接收到一个观察者的副本。* /常量订阅<年代p一个class="token operator">=可观测的<年代p一个class="token punctuation">。订阅(价值= >控制台<年代p一个class="token punctuation">。日志(“第一订阅”,价值<年代p一个class="token punctuation">));订阅<年代p一个class="token punctuation">。添加(可观测的<年代p一个class="token punctuation">。订阅(价值= >控制台<年代p一个class="token punctuation">。日志(“第二个订阅”,价值<年代p一个class="token punctuation">)));/* 5秒后取消订阅。* /setTimeout(()= >订阅<年代p一个class="token punctuation">。退订(),5000);
在这个例子中,我们应该看到,对于observable流的每个观察者,subscribe函数被调用了两次:
我们观察到(ha)创建了两个新订阅,结果值为我
是2。
注意这一点很重要,因为每个订阅都会调用subscribe函数中的代码。如果我们正在执行一个任务,比如一个HTTP请求或一个长时间运行的代码块,那么在每次创建新订阅时都将执行该任务。如果我们想避免这个,我们可以用a主题
通过订阅源可观察流,将相同的通知多播给所有的观察者。
让我们看一下<年代trong>src /演示/ subject-multicast.ts:
进口{观察者<年代p一个class="token punctuation">}从“rxjs /观察者”;进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;进口{发布<年代p一个class="token punctuation">,takeWhile<年代p一个class="token punctuation">}从“rxjs /运营商”;进口{ConnectableObservable<年代p一个class="token punctuation">}从“rxjs /可见/ ConnectableObservable”;进口“rxjs /添加/可观测间隔”;/ *组件状态。* /让活着<年代p一个class="token operator">=真正的;/*创建一个新的可观察对象,提供订阅函数。* /让我<年代p一个class="token operator">=0;常量可观测的<年代p一个class="token operator">=新可观测的()<数量<年代p一个class="token operator">>(观察者= >{控制台<年代p一个class="token punctuation">。日志(' % cNew创建订阅的,背景:# 222;颜色:# bada55”);我<年代p一个class="token operator">++;常量时间间隔<年代p一个class="token operator">=setInterval(()= >{观察者<年代p一个class="token punctuation">。下一个(我<年代p一个class="token punctuation">);},1000);返回()= >{clearInterval(时间间隔<年代p一个class="token punctuation">);};})。管(takeWhile(()= >活着<年代p一个class="token punctuation">));常量多播<年代p一个class="token operator">:ConnectableObservable<年代p一个class="token operator"><数量<年代p一个class="token operator">>=可观测的<年代p一个class="token punctuation">。发布();/*创建两个订阅多播<年代p一个class="token punctuation">。订阅(价值= >控制台<年代p一个class="token punctuation">。日志(“第一订阅”,价值<年代p一个class="token punctuation">));多播<年代p一个class="token punctuation">。订阅(价值= >控制台<年代p一个class="token punctuation">。日志(“第二个订阅”,价值<年代p一个class="token punctuation">));/*连接对象和被观察对象。* /多播<年代p一个class="token punctuation">。连接();/*在5秒后完成observable。* /setTimeout(()= >(活着<年代p一个class="token operator">=假),5000);
这一次,我们注意到subscribe函数只执行一次,所有的观察者都从源observable接收到通知:
- 首先,我们看到
订阅
函数只被调用一次,结果的值我
是1。 - 然后,每个观察者接收下一个相同值的通知:1。
- 最后,这个可观察对象在5秒后完成。
热与冷
我们要讨论的最后一个概念是确定一个可观察对象是“热的”还是“冷的”。如果一个可观察对象的生产者(或事件或值的来源)只在订阅该可观察对象时才产生一个事件或值,那么这个可观察对象就被认为是冷的。另一方面,如果生产者在可观察对象的外部,则可观察对象被认为是热的订阅
函数。换句话说,这个可观察对象关闭在一个已经产生的生产者周围。
让我们看一个冷观察到的例子<年代trong>src /演示/ observable-cold.ts文件:
进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;进口{观察者<年代p一个class="token punctuation">}从“rxjs /观察者”;进口*作为io<年代p一个class="token keyword">从“socket.io-client”;//启动socket。io年代erver via `yarn start:server`/*创建一个新的可观察对象,提供订阅函数。* /常量消息<年代p一个class="token operator">=可观测的<年代p一个class="token punctuation">。创建((观察者<年代p一个class="token operator">:观察者<年代p一个class="token operator"><任何<年代p一个class="token operator">>)= >{控制台<年代p一个class="token punctuation">。日志(' % cNew创建订阅的,背景:# 222;颜色:# bada55”);常量url<年代p一个class="token operator">=“localhost: 3000”;常量套接字<年代p一个class="token operator">:SocketIOClient<年代p一个class="token punctuation">。套接字<年代p一个class="token operator">=io(url<年代p一个class="token punctuation">);套接字<年代p一个class="token punctuation">。在(“消息”,(数据<年代p一个class="token operator">:任何)= >{观察者<年代p一个class="token punctuation">。下一个(数据<年代p一个class="token punctuation">);});返回()= >{套接字<年代p一个class="token punctuation">。断开连接();};});/*一个可观察对象是冷的,直到被订阅。* /消息<年代p一个class="token punctuation">。订阅((消息<年代p一个class="token operator">:任何)= >控制台<年代p一个class="token punctuation">。日志(消息<年代p一个class="token punctuation">));
在执行上面的示例之前,请确保启动了套接字。io服务器通过:
美元<年代p一个class="token function">纱启动:服务器
在上面的例子中,我们是:
- 使用静态创建一个可观察对象
create ()
方法,提供订阅
函数。 - 创建一个套接字。io年代erver within the
订阅
函数,这样服务器只会在observable的新观察者上创建。 - 喷出的下一个当从套接字服务器收到新消息时发出的通知。
- 在teardown函数中断开与socket服务器的连接。
因为这是一个冷观察者,所以生产者(在本例中是到套接字服务器的连接)只在调用订阅()
方法。如果注释掉创建新订阅的行,那么我们的生产者将不会产生值(在本例中是来自套接字服务器的消息)。
反过来,让我们来看看什么被认为是热的可观察到的<年代trong>src /演示/可见-热- 1. - ts:
进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;进口{观察者<年代p一个class="token punctuation">}从“rxjs /观察者”;进口*作为io<年代p一个class="token keyword">从“socket.io-client”;//启动socket。io年代erver via `yarn start:server`/*在观察者外部创建一个连接,以避免多个连接。* /让套接字<年代p一个class="token operator">:SocketIOClient<年代p一个class="token punctuation">。套接字<年代p一个class="token punctuation">;常量url<年代p一个class="token operator">=“localhost: 3000”;套接字<年代p一个class="token operator">=io(url<年代p一个class="token punctuation">);/*创建一个新的可观察对象,提供订阅函数。* /常量消息<年代p一个class="token operator">=可观测的<年代p一个class="token punctuation">。创建((观察者<年代p一个class="token operator">:观察者<年代p一个class="token operator"><任何<年代p一个class="token operator">>)= >{控制台<年代p一个class="token punctuation">。日志(' % cNew创建订阅的,背景:# 222;颜色:# bada55”);套接字<年代p一个class="token punctuation">。在(“消息”,(数据<年代p一个class="token operator">:任何)= >观察者<年代p一个class="token punctuation">。下一个(数据<年代p一个class="token punctuation">));});/*多个订阅将打开一个连接。* /// const subscription = messages。订阅((消息:任何)= >控制台。日志(“第一次订阅”,消息));/ / subscription.add(消息。订阅((消息:任何)= >控制台。日志(“第二订阅”,消息)));// setTimeout(() => subscribe .unsubscribe(), 6000);
在上述代码中:
- 我们在observable的外部创建一个到套接字服务器的连接
订阅
函数。 - 我们发出下一个当套接字发出一条消息时通知。
- 现在我们没有创建任何订阅。然而,我们的套接字服务器连接仍然建立。
- 如果取消最后几行代码的注释,我们将观察到,即使我们创建了两个订阅,也只建立了到套接字服务器的单个连接。
这是因为可观察对象已经很“热”并产生了价值。
这如何应用到Angular中呢?一个例子是HttpClientModule
。如果我们调用get ()
方法HttpClient
,我们将观察到只有当我们订阅()
对象返回的可观察对象get ()
方法。此外,我们还将观察到,对于创建的每个新订阅,都会发出一个额外的请求。这是因为多次订阅observable会创建多个生产者的实例。
最后,我们可以用分享()
操作符。我们来看看这个<年代trong>src /演示/ obserable -热- 2. - ts文件:
进口{可观测的<年代p一个class="token punctuation">}从“rxjs /可见”;进口{观察者<年代p一个class="token punctuation">}从“rxjs /观察者”;进口{分享<年代p一个class="token punctuation">}从“rxjs /运营商”;进口*作为io<年代p一个class="token keyword">从“socket.io-client”;//启动socket。io年代erver via `yarn start:server`/*创建一个新的可观察对象,提供订阅函数。* /让套接字<年代p一个class="token operator">:SocketIOClient<年代p一个class="token punctuation">。套接字<年代p一个class="token punctuation">;常量url<年代p一个class="token operator">=“localhost: 3000”;常量消息<年代p一个class="token operator">=可观测的<年代p一个class="token punctuation">。创建((观察者<年代p一个class="token operator">:观察者<年代p一个class="token operator"><任何<年代p一个class="token operator">>)= >{控制台<年代p一个class="token punctuation">。日志(' % cNew创建订阅的,背景:# 222;颜色:# bada55”);套接字<年代p一个class="token operator">=io(url<年代p一个class="token punctuation">);套接字<年代p一个class="token punctuation">。在(“消息”,(数据<年代p一个class="token operator">:任何)= >{观察者<年代p一个class="token punctuation">。下一个(数据<年代p一个class="token punctuation">);});返回()= >{套接字<年代p一个class="token punctuation">。断开连接();};})。管(分享());/*多个订阅将打开一个连接。* /常量订阅<年代p一个class="token operator">=消息<年代p一个class="token punctuation">。订阅((消息<年代p一个class="token operator">:任何)= >控制台<年代p一个class="token punctuation">。日志(“第一订阅”,消息<年代p一个class="token punctuation">));订阅<年代p一个class="token punctuation">。添加(消息<年代p一个class="token punctuation">。订阅((消息<年代p一个class="token operator">:任何)= >控制台<年代p一个class="token punctuation">。日志(“第二个订阅”,消息<年代p一个class="token punctuation">)));setTimeout(()= >订阅<年代p一个class="token punctuation">。退订(),6000);
在上面的例子中,我们使用管()
方法,提供分享()
操作符。的分享()
操作符返回一个新的多播热observable。
更多细节,请查看本的帖子热vs冷的可观测物。