rxjs的能力
rxjs是关于“流”的库,这里的流并非常规意义上的数据流,而是一类由rxjs生成的对象,它们可以向普通流一样被“订阅(subscribe)”.
从开发角度看,一切“传入回调函数”的情形均可以被转化为rxjs的流,包括但不限于普通数据流、dom.addEventListener、Array.forEach、Promise.then等.
对于流,rxjs提供了完备的方案,用于数据转化、丢弃过时结果、控制流的时序等。如果开发中遇到多个回调函数互相关联、有多个变量用于指示当前调用状态的情形,应当使用rxjs.
rxjs中的流
订阅流
// 尾部的$表明这个变量是一个rxjs流
stream$.subscribe({
next,
complete,
error,
})
// 等同于只传next
stream$.subscribe(()=>{xxx})
冷流和热流
冷流就是多次订阅都相互独立的流。以数组的foreach为例,两次调用foreach时,传入的回调函数能接受到什么值,只与当前数组的内容有关.
热流是多次订阅共享同一份数据的流,且无论有没有订阅者都会发出数据。这种流更为常见,以dom的事件为例,无论什么时候订阅了事件、订阅了多少次,都无法取到之前的事件数据,只能接受到后续触发的事件。
以上是对冷热流的初步描述,可以结合下一节的内容。
创建流
Observable
import { Observable } from 'rxjs'
const cold$ = new Observable<number>((subscriber) => {
subscriber.next(performance.now())
setTimeout(() => {
subscriber.next(2)
subscriber.error('aaa')
subscriber.complete()
subscriber.next(3) // 无效
}, 1000)
})
subscriber是流的订阅者,它会被依次接受到当前时间、数字2、一个异常.
流的状态有进行中、已完成和异常 分别对应订阅时的next complete error 且只能从进行中变为其他两个状态
除了next、complete、error,subscriber还有以下几个属性
它们的作用是:
- unsubscribe 取消当前订阅
- closed 当前订阅是否已取消
- add 将一个"finalizer"加入当前的流,在当前订阅取消时,会执行相应的操作.finalizer可以是函数,取消订阅时会被调用,可以做一些清理操作,例如取消定时器、释放dom的引用等.finalizer也可以是另一个
Subscriber,当前订阅被取消时,传入的订阅也会被取消 - remove 移除add添加的finalizer
Observable创建的流是冷流.多次订阅cold$时,构造流的函数也会被相应的调用,因此多次订阅接受的第一个值都各不相同.这就是冷流的本质:为每个订阅者各自构造数据.
Subject
const hot$ = new Subject<number>()
cold$.subscribe(hot$)
hot$.next(111)
hot$.subscribe(console.log)
Subject创建的流同时具备订阅者的能力,可以去订阅其他的流,也可以直接调用next函数输入一个数据.
可以为subject流的next属性重新赋值 以修改其接受数据的行为
Subject创建的流是热流,多次订阅共享相同的数据,每次接受数据都会同时传入给所有订阅者
从数据源创建流
rxjs提供一系列工厂源创建流 这里给出若干常用的
以下函数创建冷流
- of(a,b,c,xxx) 将args逐个输出
- interval(period) 每period毫秒输出递增的数字,不会立刻发出第一个数字.类似setInterval
- timer(deplay,period) 延迟delay毫秒后输出0,然后每隔period毫秒输出递增的数字.不传period则只输出一次
- range(0,5) 创建0,1,2,3,4的流
以下函数创建热流
- webSocket 来自rxjs/webSocket 将webSocket转化为Subject流 可以双向通信
- fromEvent(target,事件名) 将事件转化为流.target需要有addEventListener和removeEventListener,例如dom
- fromEventPattern 用于非标准事件
fromEventPattern(
(cb) => {
document.addEventListener('click', cb)
},
(cb) => {
document.removeEventListener('click', cb)
},
)
如果遇到数据源的订阅方式不是addEventListener 就可以用这个函数 自定义订阅和取消订阅的方式
from是一个特殊的工厂函数可以转化为多种类型的数据源
- from(Array/Map/Set)
- from(ReadableStream) 例如
from((await fetch('')).body) - from(其他可迭代对象)
- from(Promise) promise的值
defer的作用是延迟创建流 直到订阅时才真正创建流
let i = 0
const stream$ = defer(()=> of(i))
defer的函数可以返回一个流 也可以返回数组、Promise等流式数据源
内置的流
rxjs内置了一些工具类的流 例如立刻关闭的EMPTY 永不触发永不关闭的NEVER
冷流和热流的转化
可以调用流的.pipe函数 通过rxjs的运算符 把一个流转化为另一种形式的流
const share$ = cold$.pipe(share())
这段代码会从一个冷流获取一个数据共享的热流.和Subject作用类似,但是share会自动做资源的创建和释放
严格意义上 热流无法转化为冷流 但是可以缓存其数据 每当有新的订阅者 都将旧数据重新播放一次
const cache$ = hot$.pipe(shareReplay(Infinity))
shareReplay(n)会缓存流的n个数据 并将其重新输出给后续的订阅者
又或者可以自定义缓存机制
declare const data:any[]
const defer$ = defer(() => from(data))
defer的意思是 每当defer$被订阅 都调用() => from(data) 创建一个新流
关于冷流和热流
如果没有订阅 冷流不会启动;热流则无论是否订阅,都始终向外输出数据.