在RxJS
,数据流可以理解为经过一条一条的管道,如果这个时候,某一处的管道处理速度跟不上数据产生的速度,就会造成在内存中积压数据,相当于对上游施加了压力,在RxJS
中称之为“回压”
throttle和debounce
这两个操作符是常见的,称为“节流”和”防抖“
在浏览器中,比如resize
事件后对元素进行重新布局,但是resize
触发很频繁的话,每次都会重新布局,是十分消耗性能的,这时候就可以用这两个操作符进行处理
throttle
称为节流,可以理解为每隔一段时间内只能进行一次操作,debounce
称为“防抖”,可以理解为一段时间只能进行一次操作
比如移动鼠标,如果是throttle
的话,也许移动过程中每5秒只会调用一次回调函数,而debounce
则可以理解为如果鼠标一直在移动,则在鼠标停止的时候调用回调函数
在RxJS
里,这两个操作符分为throttle
、debounce
和throttleTime
、debounceTime
,但是只有包含Time
的操作符功能对应的是lodash
里的throttle
和debounce
throttleTime和debounceTime
这两个操作符都接受必选参数dueTime
throttleTime
import { interval } from "rxjs";import { throttleTime } from "rxjs/operators";const source$ = interval(500).pipe(throttleTime(2000));source$.subscribe(console.log);// 0// 两秒后 5// 又两秒后 10// ...
使用了throttleTime
操作符后,虽然interval
是每500毫秒吐出一条数据,但是经过了throttleTime
,在2秒的区间内,只会有一条数据吐出
debounceTime
import { fromEvent } from "rxjs";import { debounceTime } from "rxjs/operators";const source$ = fromEvent(document,"mousemove").pipe(debounceTime(200));source$.subscribe(console.log);
在上例中,只有鼠标停止移动后的200毫秒后,才会吐出数据
用数据流来控制
了解了throttleTime
和debounceTime
后,了解throttle
和debounce
就不难了
throttle
和debounce
都接受一个函数作为参数,这个函数的参数为当前的value
值,需要返回一个可被订阅的对象或者是Promise
import { interval, timer } from "rxjs";import { throttle } from "rxjs/operators";const source$ = interval(1000).pipe( throttle(value => timer(value % 3 === 0 ? 3000 : 1000)));source$.subscribe(console.log);// 0// 4// 6// 10// 12// ...
在当前值能被3整除时,下一段3秒内经过throttle
只会吐出一个值
同样可以返回Promise
const source$ = interval(1000).pipe( throttle( value => new Promise(resolve => setTimeout(resolve, value % 3 === 0 ? 3000 : 1000)) ));source$.subscribe(console.log);// 和上例输出相同
auditTime和audit
aduit
操作符和throttle
操作符类似,不过throttle
是吐出时间段内的第一条数据,aduti
是吐出时间段最后一条数据
auditTime
import { interval } from "rxjs";import { auditTime } from "rxjs/operators";const source$ = interval(1000).pipe(auditTime(2000));source$.subscribe(console.log);// 2// 5// 8
audit
import { interval, timer } from "rxjs";import { audit } from "rxjs/operators";const source$ = interval(1000).pipe(audit(() => timer(2000)));source$.subscribe(console.log);// 和上例相同
sampleTime和sample
sample就是采样的意思,sample
操作符需要从一段时间采样一个数据抛弃掉其他数据
sampleTime
sampleTime
操作符实际的表现看上去似乎和auditTime
类似,但是auditTime
操作符是上游吐出数据后,接下来的时间段吐出上游在这段时间产生的最后一个数据
而sampleTime
是按照自己的节奏吐出数据
import { interval } from "rxjs";import { sampleTime } from "rxjs/operators";const source$ = interval(300).pipe(sampleTime(500));source$.subscribe(console.log);// 0 2 3 5 7 8 10 12 ...
不管上游吐出数据有多快,sampleTime
操作符总会按照自己的时间吐出数据
sample
sample
操作符和之前的throttle
或者是audit
都不同,它接受的参数是一个Observable
对象,当这个对象产生一个数据的时候,sample
就会从上游拿最后一个产生的数据传递给下游
import { fromEvent, interval } from "rxjs";import { map, sample } from "rxjs/operators";const source$ = interval(10).pipe( map(value => value * 10), sample(fromEvent(document, "click")));source$.subscribe(console.log);
上例在点击后,sample
操作符从上游取最后一个数据,这个数据就是页面加载完成后,到点击时过去的时间
根据数据序列做回压控制
比如上游数据是0、0、1、1、1、2、2,如果说需要去掉重复的数据,只处理0、1、2,这时候,前面介绍的回压控制操作符就没用了
distinct
distinct
操作符会对上游吐出的数据去重
import { of } from "rxjs";import { distinct } from "rxjs/operators";const source$ = of(0, 0, 1, 1, 2, 1, 2, 2, 0).pipe(distinct());source$.subscribe(console.log);// 0 1 2
普通情况下distinct
操作符是使用===
比较,如果是对象的话就比较不出来了,好在distinct
操作符接收一个keySelector
函数来比较复杂类型
import { of } from "rxjs";import { distinct } from "rxjs/operators";const source$ = of( { count: 0 }, { count: 1 }, { count: 1 }, { count: 2 }, { count: 0 }).pipe(distinct(value => value.count));source$.subscribe(console.log);// { count: 0 }// { count: 1 }// { count: 2 }
distinct
操作符的问题在于,它会存储一个唯一数据集合,如果不同的数据越来越多,那么这个集合也就会越来越大,所以distinct
提供了第二个参数,用于刷新内部的唯一数据集合
import { interval } from "rxjs";import { distinct, map } from "rxjs/operators";const source$ = interval(10).pipe( map(value => (value % 2 === 0 ? 1 : 2)), distinct(undefined, interval(2000)));source$.subscribe(console.log)// 1 2// 两秒后// 1 2// ...
第二个参数interval
操作符每2秒吐出一个数据,通知distinct
刷新内部唯一数据集合,所以两秒后又会输出与之前相同的数据
distinctUntilChanged
distinctUntilChanged
操作符只会吐出与之前数据不同的数据,所以不需要维护一个内部唯一集合
import { of } from "rxjs";import { distinctUntilChanged } from "rxjs/operators";const source$ = of(1, 1, 1, 2, 2, 2, 1, 1, 2, 2).pipe(distinctUntilChanged());source$.subscribe(console.log);// 1 2 1 2
当前值与上一次吐出的值不同的时候,就会吐出当前的值
distinctUntilChanged
操作符可以接收2个可选参数,第一个参数是compare
函数,第二个参数是keySelector
函数
import { of } from "rxjs";import { distinctUntilChanged } from "rxjs/operators";const source$ = of( { age: 4, name: "Foo" }, { age: 7, name: "Bar" }, { age: 5, name: "Foo" }, { age: 6, name: "Foo" }).pipe(distinctUntilChanged((p, q) => p.name === q.name));source$.subscribe(x => console.log(x));// { age: 4, name: 'Foo' }// { age: 7, name: 'Bar' }// { age: 5, name: 'Foo' }
distinctUntilKeyChanged
distinctUntilKeyChanged
操作符可以理解为distinctUntilChanged
操作符的简写形式
import { of } from "rxjs";import { distinctUntilKeyChanged } from "rxjs/operators";const source$ = of( { name: "Foo1" }, { name: "Bar" }, { name: "Foo2" }, { name: "Foo3" }).pipe( distinctUntilKeyChanged( "name", (x, y) => x.substring(0, 3) === y.substring(0, 3) ));source$.subscribe(x => console.log(x));// { age: 4, name: 'Foo1' }// { age: 7, name: 'Bar' }// { age: 5, name: 'Foo2' }
上例比较name
字段前三个字符
其他操作符
ignoreElements
ignoreElements
操作符会忽略上游所有数据,只吐出complete
或者error
import { of } from "rxjs";import { ignoreElements } from "rxjs/operators";const source$ = of(1, 2, 3).pipe(ignoreElements());source$.subscribe({ next: console.log, complete: () => console.log("complete")});// complete
elementAt
elementAt
操作符只会取参数索引的哪一条,不过可以指定默认值
import { of } from "rxjs";import { elementAt } from "rxjs/operators";const source$ = of(1, 2, 3).pipe(elementAt(9, -1));source$.subscribe(console.log);// -1
single
single
操作符用于判断上游是否只有一条符合条件的数据,是就向下游传递数据,否则抛出一条异常
import { of } from "rxjs";import { single } from "rxjs/operators";const source$ = of(1, 2, 1).pipe(single(value => value === 1));source$.subscribe(console.log);// 不满足,抛出异常// Uncaught Sequence contains more than one element
今天修了一天年假,在家休息,轻松学习的感觉真好 突然就很想念读大学的时候,每天都无忧无虑的学习Unity3D 站在人生的十字路口,难免迷茫