RxJS学习笔记(五)辅助类操作符

辅助类操作符在RxJS中是比较特殊的操作符

数学类操作符

数学类操作符在RxJS中只有4个,分别是

  • count
  • max
  • min
  • reduce

它们的特点是,会遍历上游Observable对象中吐出的所有数据,换句话说,就是当上游数据完结时,才会给下游传递数据

count

count操作符的作用是计算上游吐出的数据个数

import { range } from "rxjs";
import { count } from "rxjs/operators";

const source$ = range(1, 20).pipe(count());
source$.subscribe(console.log);
// 20

如果上游不是立刻完结的

const source$ = interval(500).pipe(
  take(10),
  count()
);
source$.subscribe(console.log);
// 等待5秒后
// 10

由于数学类操作符会等待上游完结,所以等待5秒后,上游吐出数据,才会计算count

min和max

minmax操作符的用法是相同的,所以仅以max操作符举例

import { of } from "rxjs";
import { max } from "rxjs/operators";

const source$ = of(1, 3, 5, 4, 2).pipe(max());
source$.subscribe(console.log);
// 5
const sourceStr$ = of("ha", "ah", "xi", "zh", "zn").pipe(max());
sourceStr$.subscribe(console.log);
// "zn"

max操作符对于数字以数字大小比较,对于字符串使用ASICII码比较

除了默认的比较方式,还可以接收自定义的comparer函数来比较复杂值

const source$ = of({ value: 2 }, { value: 1 }, { value: 3 }).pipe(
  min((x, y) => x.value - y.value)
);
source$.subscribe(console.log);
// { value: 1}

reduce

reduce操作符和javascipt原生的reduce函数是一样的,只不过吐出的结果是Observable对象

import { range } from "rxjs";
import { reduce } from "rxjs/operators";

const source$ = range(1, 100).pipe(reduce((acc, value) => acc + value, 0));
source$.subscribe(console.log);

reduce接收两个参数,第一个参数是规约函数,第二个参数是种子值

规约函数接收两个参数,第一个参数是上次累积的值,第二个参数是当前的值,函数需要返回一个当前累积值给下一次计算使用

种子值也就是初始值,是可选的,默认会使用数据集合中的第一个值作为初始值,这样会跳过第一个值的计算,将第一个值作为acc参数传给第二次累积计算

reduce计算平均值

import { range } from "rxjs";
import { map, reduce } from "rxjs/operators";

const source$ = range(1, 100).pipe(
  reduce(
    (acc, value) => ({
      sum: acc.sum + value,
      count: ++acc.count
    }),
    { sum: 0, count: 0 }
  ),
  map(value => value.sum / value.count)
);
source$.subscribe(console.log);
// 50.5

条件布尔类操作符

条件布尔类操作符会根据上游Observable对象的数据和判定条件产生一个新的Observable对象,

RxJS中有5种布尔类操作符

  • every
  • find
  • findIndex
  • isEmpty
  • defaultEmpty

其中everyfindfindIndex操作符都接受一个函数参数

这个函数被称为判定函数,判定函数就是判定输入是否满足某个条件

判定函数又接收3个参数,第一个参数是当前的值,第二个参数是索引,第三个值是Observable数据源

every

every操作符和数组中的every相似,如果数据集合中的每一个值的判定结果都是trueevery操作符就会吐出true,如果有一个值不满足条件,那every操作符会立即吐出false并且不会对之后的值进行验证

import { range } from "rxjs";
import { every } from "rxjs/operators";

const source$ = range(1, 10).pipe(every(value => value > 1));
source$.subscribe(console.log);
// false

因为第一个数据1不满足条件,所以会立刻吐出false

find和findIndex

findfindIndex操作符功能也和数组的方法相同,不同的是

  • 如果数据集合中的某一个值通过验证函数,find返回的是通过的值,findIndex是返回的当前的值的索引
  • 如果数据集合中没有一个值能通过验证,find会返回undefined,而findIndex会返回-1
import { of } from "rxjs";
import { find, findIndex } from "rxjs/operators";

const sourceFind$ = of({ value: 1 }, { value: 2 }, { value: 3 }).pipe(
  find(item => item.value === 2)
);
sourceFind$.subscribe(console.log);
// { value: 2 }

const sourceFindIndex$ = of({ value: 1 }, { value: 2 }, { value: 3 }).pipe(
  findIndex(item => item.value === 5)
);
sourceFindIndex$.subscribe(console.log);
// -1

isEmpty

isEmpty操作符的作用就如其名,判定上游的Observable对象是否没有吐出数据就直接complete

import { EMPTY } from "rxjs";
import { isEmpty } from "rxjs/operators";

const source$ = EMPTY.pipe(isEmpty());
source$.subscribe(console.log);
// true

defaultEmpty

defaultEmpty操作符接收一个可选参数,这个参数是上游为empty时吐出的默认值

defaultEmpty操作符和isEmpty相同的地方在它们都会在上游Observable对象为空的时候吐出数据,不同的是isEmpty吐出的是布尔值,而defaultEmpty会吐出一个默认值

import { EMPTY } from "rxjs";
import { defaultIfEmpty } from "rxjs/operators";

const source$ = EMPTY.pipe(defaultIfEmpty("empty"));
source$.subscribe(console.log);
// empty

需要注意的一点是,如果没有给defaultEmpty传参,它会吐出一个null而不是undefined

话说有一个疑问,既然有every操作符,为什么没有some呢?