Agular 中的 RxJS 之 操作符

470 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

上一篇文章中我们介绍了许多辅助方法,能够把我们日常的很多场景转化为可观察对象,但是可能还不是很明白,我们为什么要将它们转为可观察对象?这就涉及到,许多 rxjs 提供给我们的操作符。

目前我们知道的操作符就只有一个 map,它的作用类似于数组方法里的 map,可以对数据进行操作,操作的途径就是通过方法,当然,rxjs 提供给我们的操作符不只有这一个,本文就稍微的介绍一些其他的操作符。

操作符 mapTo

对数据流进行转换,不关心原有值,可以直接传入要转换后的值。

之前我们说过的map操作符类似于数组中的map方法,能将原有的数据流通过一个函数做出改变,那么 mapTo 操作方法就更加的直接,它会直接修改原有的数据流结果,直接使得数据流流出你设定的值。

也就是会直接把流出的所有值改为常量

image.png

这里我们拿上次的 fromEvent 辅助方法来做一个 demo。fromEvent 是会将事件转为 可观察对象,并且把 event 作为数据流出,那么我们就把流出的这个数据改为一个字符串常量。

import { fromEvent } from "rxjs";
import { map, mapTo } from "rxjs/operators";

let button = document.getElementById("button");

fromEvent(button, "click")
  .pipe(mapTo("hhh"))
  .subscribe((e) => console.log(e));

image.png

可以看到原本会输出出来的 event 点击元素对象,被替换成了字符串,这就是这个操作符的作用。

操作符 pluck

获取数据流对象中的属性值。

pluck 操作符能够直接获取对象中的某一个属性

image.png

这样我们就可以用 pluck('target') 获得和之前 map 方法获取点击事件 dom 对象一样的效果:

image.png

并且在这里还需要讲述一个技巧,多个操作符是可以进行拼接的,它们会按照顺序进行操作,比方说下面这串代码,mapTo 会先将数据流转为常量,然后用 pluck 去获取里面的某一个值。

import { fromEvent } from "rxjs";
import { map, mapTo, pluck } from "rxjs/operators";

let button = document.getElementById("button");

fromEvent(button, "click")
  .pipe(
    mapTo({a:1}),
    pluck("a")
    )
  .subscribe((e) => console.log(e));

image.png

所以在我们需要的时候,可以使用多个操作符来获取我们想要的结果。

操作符 switchMap

切换可观察对象

image.png

switchMap 能够实现两个可观察对象之间的切换,那么这个切换有什么用呢。打个比方,我们可以把按钮的事件变为可观察对象,那么如果这个按钮点击之后会发送请求,那么发送的这个请求也可以是一个 可观察对象,并且我们最后需要的值是这个请求的结果,而不是点击事件,那么在这个时候,就可以使用 switchMap 在事件的可观察对象中进行切换,切换为请求的可观察对象,那么最后拿到的也会是请求的结果。

我们用之前的 from 辅助方法来转换 promise 模拟请求,把一个点击事件的可观察对象切换为请求的可观察对象。

switchMap 方法需要传入一个函数,这个函数拥有一个初始值,也就是上一个可观察对象流出的数据,然后在函数中你需要返回一个可观察对象,来作为切换的结果:

import { fromEvent, from } from "rxjs";
import { map, mapTo, pluck, switchMap } from "rxjs/operators";

function pro1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ data: 1, success: true });
    }, 2000);
  });
}

let button = document.getElementById("button");

fromEvent(button, "click")
  .pipe(
    switchMap((e) => {
      console.log(e);
      return from(pro1());
    })
  )
  .subscribe((e) => console.log(e));

image.png

可以看到,在代码中,我们把一个点击事件转化的可观察对象换成了 from 方法生成的可观察对象,并且在控制台也是成功的输出了 promise 的结果,这就说明了,可观察对象在 switchMap 方法中被替换了。

操作符 take

获取数据流中的前一部分数据

比如之前使用 from 辅助方法,或者 range 辅助方法,都是可以通过数据流不断地流出数据,那如果我们需要的数据只是前面的某一部分,就可以使用 take 操作符来做一个分隔。

image.png

我们用 from 辅助方法来转化一个数组,那么这个可观察对象会把数组中的所有属性作为数据流出,然后我们用 take 操作符做一个获取。

import { from, pipe } from "rxjs";
import { take } from "rxjs/operators";

from(["a", "b", "c"])
  .pipe(take(2))
  .subscribe((e) => console.log(e));

image.png

可以看到本来应该输出 a, b, c 结果只输出了 a 和 b 这就是 take 操作符的作用。

操作符 takeWhile

通过回调函数 获取数据流中的前一部分数据

image.png

和 take 操作符类似,不过不是传入索引来决定,而是一个回调函数。

import { from, pipe } from "rxjs";
import { take, takeWhile } from "rxjs/operators";

from([1, 2, 3])
  .pipe(takeWhile((e) => e <= 2))
  .subscribe((e) => console.log(e));

image.png

不过需要注意的是,takeWhile 的回调一旦碰到了不成立的选项就会立刻终止,不在流出下面哪怕成立的数据。这里我们稍微修改一下判断条件:

from([1, 2, 3])
  .pipe(takeWhile((e) => e != 2))
  .subscribe((e) => console.log(e));

image.png

操作符 takeUntil

接收可观察对象,当可观察对象发出值时,终止主数据源。

image.png

takeUntil 操作符 作用也是和 take 类似,都用于终止数据源,只不过传入的是一个可观察对象,当传入的可观察对象流出数据的时候,外部的数据源就会停止流出数据。

接下来我们用两个 interval 数据源来模拟一下,外部设定为 1 秒输出一次,内部设定为 3 秒一次

import { from, pipe, interval } from "rxjs";
import { take, takeWhile, takeUntil } from "rxjs/operators";

interval(1000)
  .pipe(takeUntil(interval(3000)))
  .subscribe((e) => console.log(e));

image.png

在第三秒的时候,外部数据源停止了输出,因为内部数据源输出数据了,这就是 takeUntil 的作用。

总结

本文介绍了部分操作符,这也就是我们为什么需要把日常的一些事件或者对象转化为可观察对象,因为这样就可以调用可观察对象的一些有利的特性。

在这之后,我们会在实际的一些例子中去介绍,使用可观察对象和使用一般的方法会有什么的区别,可观察对象给我们带来了什么样的操作方便。