Rxjs简单介绍
RxJS 是 Reactive Extensions for JavaScript 的缩写。是一个基于可观测数据流的响应式编程的库。
它是基于订阅-发布模式
、观察者模式
与迭代器
实现的。
响应式编程
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。 — 百度百科…
订阅-发布
项目中的 bus…
迭代器
所有的迭代器方法都必须有一个next
方法,next返回{value: '', done: 'false || true'}
。
反过来说,实现next方法接口的数据对象都可以称为迭代器。
const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]()
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: 3, done: true}
核心概念
- Observable(可观察对象)
- Observer (观察者)
- subscriber (订阅)
- Subject (Observable的特殊对象,继承了Observable)
- operators(内部操作符)
Observable
可被观察对象,提供三个方法
-
next
返回一个值,观察者可以接收到 -
error
发布一个异常的信号 -
complate
发布一个处理完成的信号
Observer
观察者,支持三个回调
- next
处理被观察者发布的值 - error
处理被观察者发布的异常 - complate
处理被观察者发布完成的信号
subscriber
订阅。将观察者订阅观察被观察者…
Subject
一个特殊的Observable实现,既可以做为被观察者又可以作为观察者。
因为Observable被订阅时,每一个订阅它的都会独立运行。
而subject则更像是一个真正的订阅管理的维护者,它搜集所有的订阅者,统一发送给他们。有点类似于dom中的addEventListener
operators
rxjs内部的一些操作数据量的方法工具,比如创建、合并、过滤、转换等等。
rxjs结合一些操作才会显得更加牛逼…
一些简单的例子
// 使用create创建一个 observable
const source = rxjs.Observable.create(observable => {
observable.next(1)
})
// 订阅
source.subscribe(v => console.log(v))
// 1
等一下… 上面的代码如果使用 Promise 写的话…
const promise = function () {
return new Promise(resolve => resolve(1))
}
promise.then(v => console.log(v))
// 1
// 看着差不多是不是...
但是问题恰恰在于,Promise 只能返回一次就完事了。
const source = rxjs.Observable.create(observable => {
observable.next(1)
observable.next(2)
})
// 订阅
source.subscribe(v => console.log(v))
// 1
// 2
并且对于失败后的处理,promise 失败了结束了,需要主动去调用
getData () {} // ....
getData().catch(_ => {
getData() // ...
})
// rxjs
const { interval, throwError, of } = rxjs
const { mergeMap, retry } = rxjs.operators
// interval 的意思是在指定时间给出一个累加的数字
const source = interval(1000).pipe(mergeMap(val => {
if (val > 5) {
return throwError('大于5了')
}
return of (val + 1)
}), retry(2)) // 这里大于5后会抛出异常,但是又 retry 了2次
source.subscribe(v => console.log(v))
// 输出 3遍 1 - 6,然后抛出异常
所以 promise 其实在处理异步中的问题并不是特别的完美。
对于观察者中 error
与 complate
的回调:
const source = rxjs.Observable.create(observable => {
observable.next(1)
observable.next(2)
observable.complete()
})
source.subscribe({
next (v) {
// ...
},
error () {
// 处理错误情况...
},
complete () {
// 完成的情况...
}
})
在一些场景中,往往是需要两个数据接口组合在一起给视图层
的。显然视图层不适合做数据的组合的,这种情况下:
const a = rxjs.of({a: 1})
const b = rxjs.of({b: 2})
// 将数据流 a 与 b 组合在一起
const c =rxjs.combineLatest(a, b).pipe(rxjs.operators.map(([a, b]) => {
return {...a, ...b}
}))
c.subscribe(v => console.log(v))
// {a: 1, b: 2}
过滤的场景:
const { interval } = rxjs
const { filter } = rxjs.operators
// 过滤可以整除2的数据
const source = interval(1000).pipe(filter(val => val % 2 === 0))
source.subscribe(v => console.log(v))
// 0 2 4 6 8...
还有其他一些操作符
- first
- last
- take
- delay
- timeInterval
特别多,不在一一列举,大概有100个左右?
详情可以看下这里https://rxjs.dev/guide/operators
场景分析
一个特别适合的例子,trello。
对于trello面板中展示的数据,可以考虑一下,他们是怎么去规划的,如何管理的数据流。
- 卡片的顺序处理或者过滤条件
- 任务列表中卡片的数据组合(任务内容、标签、相关人员等)
- 老数据与新数据的合并处理
这个过程可以反着推一下:
// 假如数据组合完毕了,只需要将数据单独过滤然后再排序
const result = source.map(filter).map(sort)
// 假如新老数据已经合并完毕
const source = rxjs.combineLatest(task, label, users).pipe(map (...a) => {
// 一堆合并操作
}) //组合数据
const task = rxjs.merge(oldtask, newtask) // 新旧的数据流合并到一起,对于下一层无感知是新还是旧
// label
// users
这样的数据下一层对上一层的数据都是无感知的,不去关心你是新的还是就的,合并没合并的,只需在数据流中向下走即可,最后result
遍到了视图层,直接渲染即可。
关于Scheduler
它相当于rxjs中的调度器,可以改变rx内部时间上的行为。
调度器 | 目的 |
---|---|
null | 不传递任何调度器的话,会以同步递归的方式发送通知。用于定时操作或尾递归操作。 |
Rx.Scheduler.queue | 当前事件帧中的队列调度(蹦床调度器)。用于迭代操作。 |
Rx.Scheduler.asap | 微任务的队列调度,它使用可用的最快速的传输机制,比如 Node.js 的 process.nextTick() 或 Web Worker 的 MessageChannel 或 setTimeout 或其他。用于异步转换。 |
Rx.Scheduler.async | 使用 setInterval 的调度。用于基于时间的操作符。 |