前言
这些笔记大部分都是阅读《深入浅出RxJS》一书的笔记。这是我第二次看了,第一次知识粗略的翻过,回想起来没有什么印象,所以决定再看一次。而这一次一边看一边做笔记,这样应该会印象更为深刻一点吧。
一、Rx.js 操作符——创建数据流
在Rx.js中,有很多操作符,从哪开始学习是一个很让人纠结的问题。而数据流是一个非常重要的概念。并且在实际的开发中,数据流就是以Observable类的实例对象而存在的,因此学习Rx.js,从创建数据流开始是一个很好的开端。
总的来说,创建数据流的操作符有以下这些:
- create 直接创建数据流
- of 根据有限的数据产生同步的数据流
- range 产生一个数值范围内的数据
- generate 以循环方式产生数据
- repeat和repeatWhen 重复产生数据流中的数据
- empty 产生空数据流
- throw 产生直接出错的数据流
- never 产生永不完结的数据流
- interval和timer 间隔给定时间持续产生数据
- from 从数组等枚举类型数据产生数据流
- fromPromise 从Promise对象产生数据流
- fromEvent和fromEventPattern 从外部事件对象产生数据流
- ajax 从Ajax请求结果产生数据流
- defer 延迟产生数据流
二、创建同步数据流
同步的数据流,顾名思义,就是产生的数据流其数据之间不存在时间间隔,因此我们无需考虑时间的问题。
2.1 create
create算是最简单的一个操作符了,它的功能非常简单,就是直接调用Observable的构造函数,其主要的逻辑如下:
Observable.create = function(subscribe) {
return new Observable(subscribe)
}
上面的代码就在Observable类的定义之中,因此我们使用create无需导入其他模块就可以直接使用了。
也正是由于这个原因,它与直接使用Observable构造函数没啥区别,因此我在大多数情况用不上它的。
2.2 public static of(values: ...T, scheduler: Scheduler): Observable<T>
我们可以利用of操作符可以创建指定个数据集合的Observable对象。比如我们要产生包含三个正整数的Observable对象,那么我们就可以这样:
import { Observable } from 'rxjs/Observable'
import 'rx.js/add/observable/of'
const source$ = Observable.of(1,2,3)
source$.subscribe(
console.log,
null,
()=>console.log('complete')
)
按照上面的代码,source$可以产生Observable对象,再被subscribe后就可以把参数1,2,3吐出来了,然后调用Observer的complete函数。需要注意的是,它吐出的数据是同步的,没有任何时间间隔。
此外,if产生的是Cold Observable,因此对于每一个Obervable都会重复突出同样的一组数据,所以可以反复使用。
of适用的场景就是已知不多的几个数据,想要把这些数据用Observable对象来封装,而且无需时间间隔,我们就能用和这个来处理。
2.3 range
range操作符顾名思义,就是能产生一个连续的数字序列。我们只需指定一个范围的开始值与长度:
const numbers = Rx.Observable.range(1, 100)
numbers.subscribe(x => console.log(x))
同样的,range产生的也是同步的数据,它会一次性的将数据给推出来。此外我们也可以让他是小数:
const numbers = Rx.Observable.range(1.5, 100)
2.4 generate
generate类似一个for循环,我们可以设定一个初始值,每次递增这个值,知道满足每个条件的时候才会终止这个循环,循环体内可以根据值产生数据,比如我们想完成上面range的那个功能,我们就可以这样:
const range = (start, count) => {
const max = start + count
return Observable.generate(
start, // 初始值
value => value < max, // 条件语句
value => value + 1, // 每次值的改变
value => value, // 产生的结果
)
}
使用generate中,四个参数分别对应了for循环中的不同表达式,除了第一个参数是一个值以外,其他三个参数都是函数,我们应该保持三个参数都是纯函数,这才不会与函数式编程的原则相违背。
此外,在generate产生不限于数值序列的数据流,字符串也是可以的:
const source = Rx.Observable.gengerate(
'x',
value => value.length <= 3,
value => value + 'x',
value => value,
)
总的来说,在Rx.js中,我们在传统编程中用到for循环产生数据的情况,我们都可以使用generate来产生我们需要的数据流。
2.5 repeat
repeat是一个实例操作符,它可以重复上游Observable中的数据若干次。比如我们想要重复的1,2,3的正整数序列10次,那么我们就能这样:
const source$ = Observable.of(1, 2, 3)
const repeated$ = source$.repeat(10)
此外,repeat的重复功能依赖于上游的完结时机,所以,使用repeat很重要的就是要保证上游Observable对象最终一定会完结,那么repeat也就没有机会unsubscribe。其次,repeat的参数代表重复的次数,因此如果不传入这个参数,或者传入的参数是附属,就代表无限次的重复。
2.6 empty
empty代表一个直接完结的Observable对象,没有参数,不会残生任何数据,直接完结。
const source = Rx.Observable.empty()
2.7 throw
throw所产生的Observable对象也是啥都不做,直接出错,而抛出的错误就是throw的参数:
const source = Rx.Observable.throw(new Error('oops'))
2.8 never
never操作符所产生的Observable对象真的啥都不做,即不突出数据,也不完结,也不会产生错误。
const source = Rx.Observable.never()
三、创建异步数据流
异步数据流,或者说异步Observable对象,是Rx.js的解决问题的一大利器,上面的那些操作符所产生的都是同步的数据流,而处理异步操作就需要使用其他的操作符了。
3.1 interval和timer
interval和timer这两个操作符的地位等同于JavaScript中的setInterval和setTimeout,但功能并不完全一样。
interval接受一个数值类型的参数,代表产生数据的间隔,返回的Observable对象就按照这个时间间隔从零开始输出递增的整数序列:
const number$ = Rx.Observable.interval(1000)
比如上面这条语句,它会从在1秒钟的时候突出数据0.然后再2秒钟的时候突出数据1.....需要注意的是,这个数据流是不会完结的,因为interval操作符不会主动调用下游的complete,要想停止这个数据序列,就必须要做退订的操作。
还有一个问题,interval本身所产生的异步数据序列只能从0开始递增,但实际中我们可以通过与其他操作符相结合来实现我们对其数据产生的定制化的需求:
const source$ = Observable.interval(1000)
const result$ = source$.map(x = x + 1)
而timer则对于着setTimeout。time的第一个参数可以是一个数值,也可以是一个Date类型的对象。如果第一个参数是数值,代表的就是毫秒数,产生的Observable对象在指定毫秒之后会突出一个数据0,然后立即完结。
const source$ = Rx.Observable.timer(1000)
上面这个功能同样也可以通过传递一个Date对象给timer来实现:
const now = new Date()
const later = new Date(now.getTime() + 1000)
const source$ = Observable.timer(later)
但我们使用Date对象作为参数,要看情况而定。如果我们明确了需要延时产生数据的时间间隔,那就应该用数值作为参数,如果明确的是一个时间点,那么就传入Date对象。
timer还支持第二个参数,它决定了各数据之间的时间间隔。如果使用它,那么就会产生一个持续突出数据的Observable对象,类似interval的数据流:
const source1$ = Rx.Obsevable.timer(2000, 1000)
const source2$ = Rx.Obsevable.interval(2000) // source1$ 和 source2$ 所产生的数据流是一样的。
3.2 from
from可以说是创建类操作符中包容性最亲那个的一个了,因为它接受的参数只要像Observable就行了,然后就能根据参数中的数据阐述一个真正的Observable对象。比如以下这些:
- 数组
- 字符串
- Promise
- generator
- 等等
const source$ = Observable.from([1, 2, 3])
3.3 fromPromise
前面说的from操作符的参数可以使Promsie对象时,当这个Promsie成功结束时,from产生的Observable对象就会突出Promsie成功的结果,并且立即结束:
const promise = Promise.resolve('good')
const source$ = Observable.from(promise)
source$.subscribe(
console.log,
error = console.log('catch', error),
() => console.log('complete')
)
这种方式只有一个结果,所以当Promsie成功完成的时候,from也不知道不会再由新的数据了,所以立即完结了产生的Observable对象。
3.4 fromEvent
fromEvent操作符在网页开发中最可能用到的操作符,以你为网页中总是要获取用户在网页中的操作时间,而fromEvent最常见的用法就是把DOM中的时间转化为Observable对象中的数据。
fromEvent接受两个参数,第一个参数是一个事件源,在浏览器中,最常见的事件源就是特定的DOM元素,第二个参数就是事件的名称,对于DOM事件就是click,mousemove这样的字符串。比如我们欧一段这样的HTML代码:
<div>
<button id='clickMe'>Click Me</button>
<div id='text'>0</div>
</div>
当我们需要完成一个点击click时,下面的数字加一的需求时,我们就可以这样做:
let clickCount = 0
const clickMe = doucment.querySelector('#clickMe')
const text = doucment.querySelector('#text')
const event$ = Rx.Observable.fromEvent(clickMe, 'click')
event$.subscribe(
() => {
text.innerText = ++clickCount
}
)
网友评论