布莱恩F爱
从俄勒冈州波特兰的一位专注yobet英雄联盟于Angular、Web技术和Node.js的谷歌开发专家那里学习。
广告 ·ultimatecourses.com.
以正确的方式学习Angular的终极课程

rxjs:基础知识

用RXJS学习角度函数反应性编程的基础知识。

为什么?

你可能会问:

Angular和函数式反应式编程有什么关系?

实际上很多。
Angular中所有异步的东西都是可观测的,这是反应式编程的核心。

如果您习惯使用承诺,可观察者真的很简单。一个简单的解释是,可观察到的是可以在一系列时间内发出多个值的承诺 - 否则称为流。当然,这有点简化,但这对我们来说是一个起点。

Meetup演讲

这篇文章主要是基于我在2018年2月落基山角会议上做的一个见面会演讲。看看YouTube上的视频:

演示文件

在这篇文章中,我将引用一些我放在一起的演示文件。你可以通过以下途径下载:

演示文件使用webpack dev-server。所有演示都包含在内SRC / DEMOS.目录,您可以切换哪些演示正在运行src / index.ts网客的入口点。只是取消注释您要执行的演示,并加载http:// localhost:8080在您的浏览器中。任何对演示文件或索引文件的更改,浏览器都会随着更新的更改实时重新加载。当然,确保一次只运行一个演示程序(为了清晰起见)。

RxJS是什么?

RxJS是ReactiveX库的JavaScript实现。ReativeX让我们作为Angular开发者能够构建异步的、模块化的、基于事件的应用。它允许我们开发这些应用程序反应性地。一开始这有点令人困惑,但当你深入RxJS时,你将开始了解这个概念是多么强大,以及RxJS提供的许许多操作符是多么强大。

回到数据流概念。承诺在哪里启用一个RxJS允许实现或拒绝的异步事件或值要观察的异步事件或值。

这是新的吗?

是的,没有。是的,这对JavaScript开发人员来说可能是新的。但是,这个概念或设计模式并不是新的。

如果你指的是著名的Gang of 4 Design Patterns书你会发现观察者设计模式

由可观察对象实现的观察者设计模式

简单解释一下概念:

  • 一种主题类包含一组观察者。
  • 每一个Concretyobserver扩展一个抽象观察者班级,实施update ()方法。
  • 对象将自己注册到主题,也可以注销自己。
  • 主题通知所有观察者的事件或价值。

什么是观察品?

简单地说,观察到实现观察者设计模式。观察者(或多个观察者)收到a通知这是可观察到的。

在RxJS中,这是由恰当命名的可观测的类,包含静态和实例方法,用于创建可观察到的类,过滤和转换值的类别,更改。此外,这是可观测的方法是围绕array.prototype.JavaScript开发人员熟悉的方法,包括:筛选()map ()reduce ()等。

来自批量生兰

为了了解我们从哪里来,到哪里去,让我们快速回顾一下承诺。简单地取消导入'./demos/promise';线src / index.ts文件。这里是内容src / demos / psumane.ts文件:

/ *创造一个新的承诺。* /常量P.=承诺解决=>{索取=>{解决“从Promiseland你好!”;}1000;};/*输出的单个值。* /P.然后价值=>控制台日志价值;

要快速审查:

  • 承诺类现在已经融入到我们的浏览器中。因此,我们不需要从库中导入任何东西。
  • 我们创造一个新的承诺,提供executor函数,接受两个参数,a解决功能和A.拒绝函数。在这个演示中,我们只使用解决函数。
  • 异步事件将导致承诺要满足或拒绝。
  • 在这个例子中,我们只是使用了setTimeout ()函数以类似于异步事件。一秒钟后,将触发字符串值“Hello from Promiseland”。

对Observableland

既然我们已经对单个异步值做出了承诺,那么可观察对象就提供了随着时间的推移而发出值流的能力。让我们看一个简单的例子src /演示/ observable.ts:

进口{可观测的}'rxjs /可观察';/*创建一个新的可观察对象,提供subscribe函数。* /常量可观测的:可观测的<字符串>=可观测的观察者=>{常量时间间隔=setInterval.=>{观察者下一个“从Observableland你好!”;}1000;// 拆除返回=>{ClearInterval.时间间隔;};};/*订阅通知。* /可观测的订阅价值=>控制台日志价值;
  • 首先,我们需要导入可观测的类作为可观察对象还不是ECMAScript (JavaScript)语言的一部分。
  • 然后,我们创造一个新的可观测的时调用的订阅函数观察者订阅可观察到的。
  • 我们可以使用next ()方法在一个观察者对象向观察者发出值。这观察者接口需要对象实现三个方法:next ()抓住()完全的()
  • 在这个例子中,我们正在使用setInterval()函数以模仿异步事件,这些事件会随着时间的推移而发出值。每一秒我们都会在唱字符串“来自观察到的Hello!”。
  • 然后,我们返回一个拆除功能,当所有观察者都没有可观察到的观察者时被调用。
  • 最后,调用订阅()方法,该方法提供每次调用的函数next ()值发送给所有观察者。

什么是a通知

通知类表示可观察流被可观察流发出的事件或值。通过可观察到的观察者推动了通知,并包括关于事件类型或值的元数据:如果事件或值是流中的下一个值,或者该值是例外,还是如果流是例外已完成,并将不再发出未来的价值。

什么是观察者

一个观察者收到一个通知从A.可观测的并由a管理订阅。观察者会反应下一个错误完成通知。

在Angular中,我们通常是框架中可观察对象发出的异步事件或值的消费者或观察者。Angular中一些常见的用例是HTTP请求和路由参数。

什么是a订阅

一种订阅是被观察对象和观察者之间的联系。控件调用时将返回一个订阅对象订阅()方法在一个可观测的。订阅对象有两个重要方法:退订()添加()。这退订()方法将从可观察对象中的观察者集合中移除一个观察者,以及任何子订阅对象。控件向现有订阅添加子订阅添加()方法。

这包括在ECMAScript中吗?

还没有。

可观察对象目前是第1阶段提案由Ecma International中的TC39小组开发的ECMAScript,它是JavaScript实现的。我们应该期待在“不久”的将来看到可观察对象被嵌入到浏览器中。

在那之前,我们需要使用RxJS之类的库。而且,在撰写本文时,可观察对象提议并不包含RxJS所包含的许多反应式编程操作符。因此,在可预见的未来,我们可能会使用RxJS。

什么是a主题

一种主题是一个专门的可观察到,使我们能够对许多观察者进行多播价值,并且有几种实施的行为。因为这主题类继承了可观测的它继承了observable的所有相同的方法和属性。所以,主体既是可观察的也是观察者。这是我们需要学习的一个重要而有力的概念。

让我们来看看src /演示/ subject.ts:

进口{主题}“rxjs /主题”;/*创建一个Subject实例。* /常量S.=主题<数字>;/ *订阅主题。* /S.订阅下一个=>控制台日志'在1之前:'下一个错误=>控制台警告错误=>控制台日志“之前完成1”;S.订阅下一个=>控制台日志'在2之前:'下一个错误=>控制台警告错误=>控制台日志2之前完成的;/*触发一些值。* /S.下一个1;S.下一个2;S.下一个3.;/ *订阅迟到的主题。* /S.订阅下一个=>控制台日志”后,“下一个错误=>控制台警告错误=>控制台日志'完成后';/*延迟订阅将收到通知。* /S.下一个4.;S.完整的;

在这个例子中:

  • 我们进口主题类,然后创建一个新实例。
  • 我使用的是TypeScript,所以我为这个主题指定了泛型类型数字。这意味着我可以期望由可观察流发出的值是数字。
  • 接下来,我们创建两个新订阅。我们为所有这三个通知提供了一个回调函数:下一个完成,按此顺序作为参数订阅()方法。
  • 我只是登出通知到控制台的结果。
  • 然后,使用next ()方法:1,2和3。
  • 然后在发出前三个值之后添加第三个订阅。
  • 然后,我们使用的是第四个价值next ()方法:4。
  • 最后,我们完全的()可观察的流,通知观察者流以后不会再发出值。

如果你检查你的控制台,你应该看到:

前1:1在2:1前1:2 2:前2前1:3之前2:前3 1:2:前4后4:4完成之前1之前完成2完成之后

请注意,订阅可观察事项的顺序,因为第三订阅仅接收第四值(4)。另请注意,因为一个主题是可观察的,我们可以举援next ()向观察者发出附加值的方法。

什么是AsyncSubject

AsyncSubject类继承了主题的所有方法和属性主题。而且,别忘了主题扩展obs

AsyncSubject是三种不同之一主题由RXJS库实施的行为。我们会看看所有这些。

这是一个例子,位于src / demos / sacync-subject.ts:

进口{AsyncSubject}“rxjs / asyncsubject”;/*创建一个AsyncSubject实例。* /常量S.=AsyncSubject<数字>;/ *订阅主题。* /S.订阅下一个=>控制台日志“:”下一个错误=>控制台警告错误=>控制台日志之前完成的;/*触发一些值。* /S.下一个1;S.下一个2;S.下一个3.;/ *订阅迟到的主题。* /S.订阅下一个=>控制台日志”后,“下一个错误=>控制台警告错误=>控制台日志'完成后';/*完成可观察流。* ///我们必须完成,以便发送值给订阅S.完整的;

这个例子与前面的例子非常相似主题类。主要的区别是我们的AsyncSubject将只接收最后发出的值,并且仅在完成时。

在你的控制台中你应该看到:

之前:3之后:3完成后完成后完成

请注意,订阅可观察流的顺序无关。您也可以通过评论来解决这个例子完全的()方法调用。当你这样做时,当观察者从未收到通知时,您应该在控制台中看到任何内容。

什么是a行为赞许

当观察者订阅A行为赞许它收到可观察到的最新通知,然后继续接收未来通知,直到流完成。为了实现这一目标,我们必须在创建新时提供种子(或默认)值行为赞许

让我们看一个例子SRC / DEMOS /行为主题.TS文件:

进口{行为赞许}“rxjs / BehaviorSubject”;/*创建一个BehaviorSubject实例。* /常量S.=行为赞许<数字>0.;/ *订阅主题。* /S.订阅下一个=>控制台日志“:”下一个错误=>控制台警告错误=>控制台日志之前完成的;/*触发一些值。* /S.下一个1;S.下一个2;S.下一个3.;// s.complete();/ *订阅迟到的主题。* /S.订阅下一个=>控制台日志”后,“下一个错误=>控制台警告错误=>控制台日志'完成后';

要快速审查:

  • 首先,我们导入行为赞许类,然后创建一个提供种子值的新实例。
  • 接下来,我们订阅了可观察的流。
  • 然后,我们发出三个值:1,2,3。
  • 然后我们再次订阅(在排除了三个值后)。

以下是登录到控制台的原因:

Before: 0 Before: 1 Before: 2 Before: 3 after: 3

以下几点需要注意:

  • 请注意在调用之前创建的订阅next ()收到种子值0。
  • 并注意我们在我们被授予第三个价值(3)之后创建的订阅也收到了最新通知。
  • 如果取消对调用的行进行注释完全的()请注意,第二个订阅从未收到过下一个通知,但仍然接收完成通知。

什么是aReplaySubject

顾名思义,aReplaySubject将源可观察对象发出的所有通知发送给任何观察者,而不管观察者何时订阅。

让我们来看看一个例子src /演示/ replay-subject.ts:

进口{ReplaySubject}“rxjs / ReplaySubject”;/*创建ReplaySubject实例。* /常量S.=ReplaySubject<数字>;/ *订阅主题。* /S.订阅下一个=>控制台日志“:”下一个错误=>控制台警告错误=>控制台日志之前完成的;/*触发一些值。* /S.下一个1;S.下一个2;S.下一个3.;/ *订阅迟到的主题。* /S.订阅下一个=>控制台日志”后,“下一个错误=>控制台警告错误=>控制台日志'完成后';/*完成可观察流。* /S.完整的;

上面的代码与前一个示例非常相似。注意我们发出了完成两次订阅后通知。

下面是输出到控制台的内容:

Before: 1 Before: 2 Before: 3 after: 1 after: 2 after: 3 complete Before complete after

在此示例中,无论创建订阅时,两个订阅都会接收所有通知。

单播vs组播

一个可观测的是单播,因为每个订阅观察者都拥有独立执行可观察到的。

让我们看一个示例,该示例将向我们展示对于每个订阅我们将执行订阅函数提供给可观测的构造函数()函数。

示例位于SRC / DEMOS / over-Unicast.ts文件:

进口{可观测的}'rxjs /可观察';/*创建一个新的可观察对象,提供subscribe函数。* /一世=0.;常量可观测的:可观测的<数字>=可观测的观察者=>{控制台日志' % cNew创建订阅的背景:# 222;颜色:# bada55”;一世++;常量时间间隔=setInterval.=>{观察者下一个一世;}1000;返回=>{ClearInterval.时间间隔;};};/*每次订阅都会收到一份Observer。* /常量订阅=可观测的订阅价值=>控制台日志“第一订阅”价值;订阅添加可观测的订阅价值=>控制台日志“第二个订阅”价值;/ * 5秒后取消订阅。* /索取=>订阅取消订阅5000;

在此示例中,我们应该看看Subscribe函数是否已调用两次,每个观察者都有两次可观察流:

默认情况下,可观察对象是单播的

我们观察到(HA)是创建了两个新的订阅,并产生了结果一世是2。

注意这一点很重要,因为订阅函数中的代码将为每次订阅调用。如果我们正在执行HTTP请求或长时间运行的代码块等任务,则每次创建新订阅时都会执行此操作。如果我们想避免这个,我们可以用a主题使用单个订阅来组播与所有观察者的相同通知,使用单个订阅源可观察流。

让我们看一下SRC / DEMOS /主题 - 组播:

进口{观察者}“rxjs /观察者”;进口{可观测的}'rxjs /可观察';进口{发布那takeWhile}'rxjs / operators';进口{connectberablebservable.}'rxjs /可观察/ connectbatebservable';进口'rxjs /添加/可观察/间隔';/ *组件状态。* /活着=真正的;/*创建一个新的可观察对象,提供subscribe函数。* /一世=0.;常量可观测的=可观测的<数字>观察者=>{控制台日志' % cNew创建订阅的背景:# 222;颜色:# bada55”;一世++;常量时间间隔=setInterval.=>{观察者下一个一世;}1000;返回=>{ClearInterval.时间间隔;};}takeWhile=>活着;常量多播:connectberablebservable.<数字>=可观测的发布;/ *创建两个订阅* /多播订阅价值=>控制台日志“第一订阅”价值;多播订阅价值=>控制台日志“第二个订阅”价值;/*连接主语和观察者。* /多播连接;/* 5秒后完成观察* /索取=>活着=错误的5000;

这次我们注意到subscribe函数只执行了一次,所有的观察者都接收到来自源observable的通知:

多播主题

  • 首先,我们看到订阅函数只被调用一次,其结果是一世是1。
  • 然后,每个观察者接收下一个相同值的通知:1。
  • 最后,可观察到5秒后完成。

热与冷

我们将讨论的最后一个概念是确定可观察者是否被达成为“热”或“冷”。如果生产者(或事件或价值的来源)将仅在订阅到可观察到的情况下仅生成事件或价值,则认为可观察到的可感冒。另一方面,如果生产者在可观察到的情况之外,则认为可观察到的是热的订阅函数。换句话说,可观察到的围绕已经生产的生产者结束。

让我们来看看寒冷观察的一个例子src /演示/ observable-cold.ts文件:

进口{可观测的}'rxjs /可观察';进口{观察者}“rxjs /观察者”;进口*作为IO.“socket.io-client”;//确保启动socket。IO.S.erver via `yarn start:server`/*创建一个新的可观察对象,提供subscribe函数。* /常量消息=可观测的创造观察者观察者<任何>=>{控制台日志' % cNew创建订阅的背景:# 222;颜色:# bada55”;常量url='localhost:3000';常量套接字:socketioclient.。插座=IO.url;套接字'信息'数据任何=>{观察者下一个数据;};返回=>{套接字断开连接;};};/ *可观察到的是冷,直至订阅。* /消息订阅信息任何=>控制台日志信息;

在执行上面的示例之前,请务必通过以下方式启动Socket.io服务器:

$纱开始:服务器

在上面的例子中,我们是:

  • 使用静态创建一个可观察对象create ()方法,提供订阅函数。
  • 创建一个套接字。IO.S.erver within the订阅函数,使服务器只在一个新的可观察对象上被创建。
  • 发出下一个当从套接字服务器接收到新消息时的通知。
  • 在teardown功能中断开与socket服务器的连接。

因为这是一个冷观察者,所以生产者(在本例中是到套接字服务器的连接)只有在调用订阅()方法。如果您注释掉创建新订阅的行,那么我们的生产者将不会产生一个值(在本例中是来自套接字服务器的消息)。

相反,让我们来检查被认为是一个热的可观察SRC / DEMOS /可观察 - 热 - 1.TS:

进口{可观测的}'rxjs /可观察';进口{观察者}“rxjs /观察者”;进口*作为IO.“socket.io-client”;//确保启动socket。IO.S.erver via `yarn start:server`/ *在观察者外面创建单个连接,以避免多个连接。* /套接字:socketioclient.。插座;常量url='localhost:3000';套接字=IO.url;/*创建一个新的可观察对象,提供subscribe函数。* /常量消息=可观测的创造观察者观察者<任何>=>{控制台日志' % cNew创建订阅的背景:# 222;颜色:# bada55”;套接字'信息'数据任何=>观察者下一个数据;};/ *多订阅将打开单个连接。* /// const subscription =消息。订阅((信息:任何)=>控制台。日志(“第一订阅”那信息));// subscription.add(messages.subscribe((message:any)=> console.log('第二订阅',消息)));// setTimeout(() => subscribe .unsubscribe(), 6000);

在上述代码中:

  • 我们在可观察对象之外创建一个到套接字服务器的连接订阅函数。
  • 我们发出下一个每次套接字发出消息时都会通知。
  • 现在我们没有创建任何订阅。但是,我们的套接字服务器连接仍然已建立。
  • 如果您取消了最后几行代码,我们将观察到,即使我们正在创建两个订阅,我们只能建立与套接字服务器的单个连接。

这是因为可观察到的已经“热”并产生价值。

这如何应用于Angular呢?一个例子是HttpClientModule。如果我们调用get ()对策httpclient,我们将观察到获取网络请求仅在我们时创建订阅()对象返回的可观察对象get ()方法。此外,我们还将观察到,对于创建的每个新订阅,都进行了额外的请求。这是因为订阅可观察到的多次将创建生产者的多个实例。

最后,我们可以使用分享()操作符。让我们看看这个src /演示/ obserable -热- 2. - ts文件:

进口{可观测的}'rxjs /可观察';进口{观察者}“rxjs /观察者”;进口{分享}'rxjs / operators';进口*作为IO.“socket.io-client”;//确保启动socket。IO.S.erver via `yarn start:server`/*创建一个新的可观察对象,提供subscribe函数。* /套接字:socketioclient.。插座;常量url='localhost:3000';常量消息=可观测的创造观察者观察者<任何>=>{控制台日志' % cNew创建订阅的背景:# 222;颜色:# bada55”;套接字=IO.url;套接字'信息'数据任何=>{观察者下一个数据;};返回=>{套接字断开连接;};}分享;/ *多订阅将打开单个连接。* /常量订阅=消息订阅信息任何=>控制台日志“第一订阅”信息;订阅添加消息订阅信息任何=>控制台日志“第二个订阅”信息;索取=>订阅取消订阅6000;

在上面的例子中,我们使用管()方法,提供分享()操作符。这分享()操作符返回一个新可观察对象,该可观察对象是多播的和热的。

有关更多详细信息,请查看Ben的帖子HOT VS冷的可观察品

布莱恩F爱

嗨,我是布莱恩。我对TypeScript, Angular和Node.js感兴趣。我和我最好的朋友邦妮结婚了,我住在波特兰,我经常滑雪。