学习rxjs 1.rxjs的能力和一些概念

91 阅读3分钟

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还有以下几个属性 image.png
它们的作用是:

  • 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) 创建一个新流

关于冷流和热流

如果没有订阅 冷流不会启动;热流则无论是否订阅,都始终向外输出数据.