rxjs 获取接口示例_10个需要了解的RxJS函数和示例

rxjs 获取接口示例

这篇文章是由同行评审弗洛里安Rappl莫里茨克罗格 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态!

随着对函数式React式编程 (FRP)的兴趣的增长, RxJS已经成为该范例中最流行JavaScript库之一。 在本文中,我们将看一看我认为RxJS的十个必须知道的函数。

注意:本文假定您熟悉RxJS的基础知识,如使用RxJS进行函数式无功编程简介中所述

React式编程

响应式编程是一种编程范例,将称为Observables的数据流作为其基本编程单元。

流(或RxJS术语中的Observables)类似于事件侦听器:两者都等待事件发生,并在事件发生时通知您。 您从onClick侦听器获得的一系列异步通知是数据流的完美示例。

换句话说,一个Observable就是随时间填充数组

该数组的元素几乎可以来自任何地方:文件系统,DOM事件,API调用,甚至是转换后的同步数据,例如数组。 从本质上讲,React式编程无非就是将Observables用作程序的构建基块。

与数组的关系

数组很简单,因为除非明确更改,否则它们的内容是最终的。 从这个意义上讲,数组本质上没有时间性。

另一方面,Observable由时间定义。 关于流,您所知道的最多是它到目前为止已收到[1, 2, 3] 。 您无法确定是否会得到4 ,也不会得到4 ,这是由数据源而不是程序决定的。

流和数组之间的关系是如此深刻,以至于大多数React式扩展都源自函数式编程领域,其中列表操作是面包和黄油。

热身RxJS

考虑所有常见的待办事项应用程序。 让我们看看用RxJS看起来只显示用户未完成任务的名称的问题:

const task_stream =
  // Makes a stream of all the tasks in the database
  getTasks().
    // Get tasks only for this user
    filter((task) => task.user_id == user_id).
    // Get tasks that are incompleted
    filter((task) => !task.completed).
    // Only get name of task
    map((task) => task.name)

/* Tasks look like this:
   task = {
    user_id   : number,
    completed : boolean,
    name      : string
   }
 */

到目前为止,这不过是数组的额外功能 ,而是展示了React式编程的功能风格。

通过添加更复杂的“真实世界”功能,声明性变得更加清晰。 假设我们要:

  • 响应用户对查看已完成或未完成任务的选择,启动请求;
  • 仅每秒发送一次对最后选择的请求,以免在用户快速更改选择时浪费带宽;
  • 重试失败的请求,最多三遍; 和
  • 仅当服务器发送与上次不同的响应时,才重新绘制视图。
const task_stream =
  parameter_stream.
    debounce(1000).
    map((parameter) => {
      getTasks().
        retry(3).
        filter((task) => task.user_id === user_id).
        filter((task) => task.completed === parameter).
        map((task)    => task.name)
    }).
    flatMap(Rx.Observable.from).
    distinctUntilChanged().
    update()

一步步:

  • parameter_stream告诉我们用户是要完成任务还是完成任务,将选择存储在parameter
  • debounce()确保我们仅注意每秒的最后一次单击按钮;
  • getTasks()周围的部分与之前相同;
  • distinctUntilChanged()确保我们仅在服务器响应与上次响应不同时注意。 和
  • update()负责更新UI以反映我们从服务器获得的内容。

以强制性的,基于回调的方式处理去抖动,重试和“直到更改之前是唯一的”逻辑是有效的,但它既脆弱又复杂。

得出的结论是,使用RxJS进行编程可以实现:

  1. 声明式程序;
  2. 可扩展的系统; 和
  3. 简单,强大的错误处理。

我们将通过RxJS的十个必知功能在上例中满足上述示例中的每个功能。

简单流上的操作

简单流(发出诸如字符串之类的简单值)的基本函数包括:

除了take()takeWhile() ,它们类似于JavaScript的高阶数组函数。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

我们将通过解决一个示例问题来应用所有这些方法:在数据库中查找拥有.com或.org网站的所有用户,并计算其网站名称的平均长度。

JSONPlaceholder将成为我们的用户来源。 这是我们将使用的用户数据的JSON表示形式

1.使用map()转换数据

在Observable上使用map()等同于在数组上使用它。 它:

  1. 接受回调作为参数;
  2. 在调用它的数组的每个元素上执行它; 和
  3. 返回一个新数组,该数组将原始数组的每个元素替换为对其调用回调的结果。

在Observables上使用map()的唯一区别是:

  1. 它返回新的Observable而不是返回新的数组; 和
  2. 它在Observable每次发出新项目时执行,而不是立即全部执行。

我们可以使用map()将用户数据流转换成他们网站名称的列表:

source.
  map((user) => user.website)

请参见RxJS的Pen 10函数//通过CodePen 的SitePoint( @SitePoint )进行映射

在这里,我们使用map来将传入流中的每个用户对象“替换”为每个用户的网站。

RxJS还允许您将map()称为select() 。 这两个名称指的是相同的功能。

2.筛选结果

map()filter()在Observables上与在数组上几乎相同。 为了找到每个使用.net或.org网站地址的用户,我们可以这样写:

source.
  map((user) => user.website).
  filter((website) => (website.endsWith('net') || website.endsWith('org'));
})

请参见RxJS的Pen 10函数//通过CodePen上的SitePoint( @SitePoint )进行过滤

这仅选择其网站以“ net”或“ org”结尾的用户。

filter()也具有别名where()

3.使用reduce()收集结果

reduce()允许我们使用所有单独的值并将它们转换为单个结果。

reduce()往往是基本列表操作中最令人困惑的地方,因为与filter()map()不同,它的行为因使用而异。

通常, reduce()接受值的集合,并将其转换为单个数据点。 在我们的例子中,我们将向它提供一个网站名称流,并使用reduce()将其转换为一个对象,该对象将统计我们找到的网站数量以及其名称长度的总和。

source.
  map((user) => user.website).
  filter((website) => (website.endsWith('net') || website.endsWith('org'))).
  reduce((data, website) => {
    return {
      count       : data.count += 1,
      name_length : data.name_length += website.length
    }
  }, { count : 0, name_length : 0 })

见笔从RxJS 10功能//减少由SitePoint( @SitePoint上) CodePen

在这里,我们将流简化为单个对象,该对象跟踪:

  1. 我们看过多少个网站; 和
  2. 他们所有名字的总长度。

请记住, reduce()仅在源Observable完成时才返回结果。 如果您想在每次流接收到新项时都知道累加器的状态,请改用scan()

4.使用take()限制结果

take()takeWhile()完善简单流上的基本功能。

take(n)从流中读取n值,然后取消订阅。

每当我们收到一个网站时,我们都可以使用scan()发出我们的对象,并且只take()前两个值。

source.
  map((user) => user.website).
  filter((website) => (website.endsWith('net') || website.endsWith('org'))).
  scan((data, website) => {
      return {
        count       : data.count += 1,
        name_length : data.name_length += website.length
      }
    }, { count : 0, name_length : 0 }).
  take(2);

请参阅RxJS的Pen 10函数// //通过CodePen上的SitePoint( @SitePoint )进行扫描和获取/ 获取

RxJS还提供了takeWhile() ,它允许您获取值,直到某些布尔测试成立。 我们可以像这样用takeWhile()编写上面的流:

source.
  map((user) => user.website).
  filter((website) => (website.endsWith('net') || website.endsWith('org'))).
  scan((data, website) => {
    return {
      count       : data.count += 1,
      name_length : data.name_length += website.length
    }
  }, { count : 0, name_length : 0 }).
  takeWhile((data) =>  data.count < 3)

高阶流的运算

除了它们在Observables(而不是数组)上工作之外,这些函数与熟悉的list操作几乎相同。

“ [如果您知道如何使用Array#extras对数组进行编程,那么您已经知道如何使用RxJS!” 〜RxJS文档

正如数组可以包含比简单值(例如数组或对象)更复杂的数据一样,可观察对象也可以发出更高阶的数据,例如承诺或其他可观察对象。 这是更多专门工具起作用的地方。

5.用flatMap()压缩流

。 。 。 事实上,我们已经在使用一个!

定义source时,我们对fromPromise()flatMap()进行了调用:

const source   =
        // Take a Promise and convert it to an Observable
        Rx.Observable.fromPromise(makeRequest(ENDPOINT))
          // Flatten Promise
          .flatMap(Rx.Observable.from);

这使用了三台新机器:

  1. 来自Promise ;
  2. Rx.Observable.from ; 和
  3. flatMap
从诺言中观察到的

一个Promise代表一个单个的将来值,我们将异步获取该值,例如,调用服务器的结果。

Promise的定义特征之一是它仅代表一个未来价值。 它不能返回多个异步数据; 这就是Observables所做的,并且是两者之间的根本区别。

这意味着,当我们使用Rx.Observable.fromPromise() ,我们将获得一个发出单个值的Observable,即:

  1. Promise解决的价值; 要么
  2. Promise拒绝的错误。

当Promise返回字符串或数字时,我们不需要做任何特殊的事情。 但是,当返回数组时(如本例所示),我们更喜欢创建一个Observable来发出数组的内容,而不是将数组本身作为单个值发出。

6.使用flatMap()

此过程称为flattening, flatMap()负责此过程。 它有很多重载 ,但我们只会使用最简单和最常见的重载

使用flatMap() ,我们:

  1. 在Observable上调用flatMap() ,它发出单值分辨率或Promise拒绝; 和
  2. 将其传递给函数以创建新的Observable。

在我们的例子中,我们传递Rx.Observable.from() ,它根据数组的值创建一个序列:

Rx.Observable.from([1, 2, 3]).
  subscribe(
      onNext (value) => console.log(`Next: ${value}`))

// Prints: 
//  Next: 1
//  Next: 2
//  Next: 3

这涵盖了我们小前奏中的代码:

const source =
  // Create an Observable emitting the VALUE or REJECTION of a Promise...
  Rx.Observable.fromPromise(makeRequest(ENDPOINT))
    // ...And turn it into a new Observable that emits every item of the
    //  array the Promise resolves to.
    .flatMap(Rx.Observable.from)

RxJS也具有flatMap()的别名: selectMany()

组成多个流

通常,我们需要将多个流放在一起。 合并流的方法有很多,但有几种方法比其他方法要多。

7.将流与concat()和merge()合并

串联和合并是合并流的两种最常用方法。

串联通过发出第一个流的值直到完成为止,然后发出第二个流的值来创建新流。

合并通过从活动流中发出值来从许多流中创建一个新流

考虑在Facebook Messenger上一次与两个人交谈。 concat()是您从两个人都收到消息,但先完成与一个人的对话,然后再回复另一个人的方案。 merge()类似于创建群聊并同时接收两个消息流。

source1.
  concat(source2).
  subscribe(
    onNext(value) => console.log(`Next: ${value}`))
    // Prints 'Source 1' values first, THEN 'Source 2'

source1.
  merge(source2).
  subscribe(
    onNext(value) => console.log(`Next: ${value}`))
    // INTERLEAVES 'Source 1' and 'Source 2' values

请参阅CodePen上的SitePoint( @SitePoint )的RxJS的Pen 10函数// merge&concat

concat()流将打印所有从值的source1第一次,才开始从印刷值source2source1完成。

merge()流将打印从值source1source2 ,因为它接收它们:从所述第二发光值之前它不会等待第一流以完整的。

8.使用switch()

通常,我们想听一个Observable发出的Observable,但是只注意源中的最新发出。

为了进一步扩展Facebook Messenger的类比,在您使用switch()的情况下。 。 。 好了,根据当前正在发送消息的人来切换响应的人。

为此,RxJS提供了switch

用户界面为switch()提供了几个很好的用例。 如果我们的应用程序在用户每次选择要搜索的内容时都触发请求,则可以假定他们只想查看最新选择的结果。 因此,我们使用switch()仅侦听最新选择的结果。

在进行此操作时,我们应该确保不要浪费带宽,只需点击服务器以获取用户每秒进行的最后一次选择。 我们为此使用的函数称为debounce()

如果您想朝另一个方向前进,并且只接受第一个选择,则可以使用油门() 。 它具有相同的API,但行为相反。

看到笔从RxJS 10功能//开关,combineLatest,&distinctUntilChanged由SitePoint( @SitePoint上) CodePen

9.协调流

如果我们要允许用户搜索帖子或具有特定ID的用户怎么办?

为了演示,我们将创建另一个下拉列表,并允许用户选择他们想要检索的项目的ID。

有两种情况。 当用户:

  1. 更改任一选择; 要么
  2. 更改两个选择。
使用CombineLatest()响应对任一流的更改

在前一种情况下,我们需要创建一个流来触发具有以下内容的网络请求:

  1. 用户最近选择的哪个端点; 和
  2. 用户最近选择的ID。

。 。 。 并在用户更新任一选择时执行此操作。

这是combineLatest()的作用:

// User's selection for either POSTS or USERS data
const endpoint_stream = 
  Rx.Observable.fromEvent(select_endpoint, 'click').
    map(event  => event.target).
    map(target => (target.options[target.selectedIndex].text.toLowerCase()));

// Which item ID the user wants to retrieve
const id_stream = 
  Rx.Observable.fromEvent(select_id, 'click').
    map(event  => event.target).
    map(target => (target.options[target.selectedIndex].text));

// Emits a pair of the most recent selections from BOTH streams 
//   when EITHER emits a value
const complete_endpoint_stream = 
  endpoint_stream.combineLatest(id_stream);

请参见RxJS的Pen 10函数//在CodePen上由SitePoint( @SitePoint合并及最新和 uniqueUntilChanged

每当其中一个流发出一个值时, combineLatest()都会获取发出的值,并将其与另一个发出的流的最后一项配对,然后在数组中发出该对。

这在图表中更容易可视化:

// stream1 : Emits 1
// stream2 : Emits 1

combined : Emits [1, 1]

// stream2: Emits 2

combined : Emits [1, 2]

// stream2: Emits 3

combined : Emits [1, 3]
仅使用zip响应两个流中的更改

要等到用户更新其对ID和Endpoint字段的选择时,请将combineLatest()替换为zip()

同样,通过图表更容易理解:

// stream1 : Emits A
// stream2 : Emits 1
zipped : Emits [A, 1]

// stream2: Emits 2
zipped : Emits NOTHING

// stream2: Emits 3
zipped : Emits NOTHING

// stream1: Emits B
zipped : Emits [B, 2]

// stream1: Emits C
zipped : Emits [C, 3]

combineLatest()不同, zip()会等到两个Observable都发出新的东西之前,才发出其更新值数组。

10. takeUntil

最后, takeUntil()允许我们监听第一个流,直到第二个开始发出值为止。

source1.
  takeUntil(source2);

当您需要协调流,但不一定要合并它们时,这很有用。

结语

向数组添加时间维度的简单事实为全新的程序思考方式打开了大门。

RxJS的功能远不止我们在这里看到的,但这远远超出了我们的预期

开始使用RxJS Lite ,方便使用文档花点时间弄乱双手。 在不知不觉中,一切看起来都像是溪流。 。 。 因为一切都是。

翻译自: https://www.sitepoint.com/rxjs-functions-with-examples/

rxjs 获取接口示例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值