概述
RxJS 是一个使用可观察序列编写异步和基于事件的程序的库。它提供了一种核心类型,即 Observable、一些周边类型(Observer、Scheduler、Subjects)和类似于 Array
方法(map
、filter
、reduce
、every
等)的操作符,以便将异步事件作为集合进行处理。
可以将 RxJS 视为处理事件的 Lodash。
RxJS 中解决异步事件管理的基本概念有:
- Observable(可观察者) 表示未来(future)值或事件的可调用集合的概念。
- Observer(观察者) 是一个回调集合,它知道如何监听 Observable 传来的值。
- Subscription(订阅) 表示 Observable 的一次执行,主要用于取消执行。
- Operator(操作符) 是纯函数,可以使用
map
、filter
、concat
、reduce
等操作来以函数式编程风格处理集合。 - Subject(主体: 相当于一个 EventEmitter,也是将一个值或事件多播到多个 Observers 的唯一方式。
- Scheduler(调度器) 是控制并发的集中化调度器,允许我们在计算发生时进行协调,例如
setTimeout
或requestAnimationFrame
或其它。
让我们用简单的代码理解一下上面的概念
Observable-Observer-Subscription关系
js
const { fromEvent, filter } from 'rxjs'
const click$ = fromEvent(document, 'click') // 从文档的点击事件创建了Observable,即可观察者
const observer = { next: (event) => console.log(event) } // 创建Observer, 即观察者,包含一个 `next` 方法用于记录事件
const subs = click$ // 建立订阅对象,并传入观察者对象
.pipe(filter((e) => e.clientY > 100)) // filter操作符,当返回结果为false时,不会触发后面的订阅函数
.subscribe(observer); // 必须
subs.unsubscribe(); // 取消订阅
通俗一点讲就是,我们要观察document的点击事件(Observable),观察到了,我们就触发log方法(observer),但是我们只有通过pipe(管道)连接两者,并订阅(Subscription),才会通知到Observer。
用个例子,我要你去偷听一个人打电话然后告诉我,打电话就是可观察者(Observable),你就是观察者(Observer)观察到了之后干啥呢,告诉我(next事件),我要你去偷听是订阅(Subscription),如果没有订阅,你不会去听。中间你对我叙述听到的内容,你可以简短了说,也可以添油加醋的说,这就是中的操作符,你怎么操作的我不管,反正我听到的是你告诉我的内容。
说一下主体Subject
js
const { fromEvent, filter, Subject, take } from 'rxjs'
const subject = new Subject(); // 创建主体对象,之后主要靠主体对象来进行广播
const click$ = fromEvent(document.getElementsByTagName('h1'), 'click');
// take是设定事件只触发两次,后面是将click$全部交给subject主体进行广播
click$.pipe(take(2)).subscribe(subject);
// 最后在有subject去建立Observe连接
const subs1 = subject.subscribe((e) => console.log(1));
const subs2 = subject.subscribe((e) => console.log(2));
subs1.unsubscribe(); // 取消订阅
subs2.unsubscribe(); // 取消订阅
广播顾名思义,就是一对多。 用上面的例子理解就是:subject理解为一个中介,我让你去听电话,他让你去看别人在干嘛,我们不直接对发号施令,而是通过中介。
调度器Schedule
在RxJS中,Schedulers(调度器)是用于控制 Observable 执行时的执行上下文(例如同步或异步)的一种机制。调度器允许你明确地定义 Observable 在何时、何地以及如何执行其操作。
简单说就是用来控制事件发出的顺序和速度的(发送给观察者的)。它还可以控制订阅 ( Subscriptions ) 的顺序。
RxJS 提供了几个内置的调度器,其中常见的包括:
-
queueScheduler
(队列调度器):使用
queueScheduler
时,操作会被放入一个队列,并在事件循环的下一个时刻执行。这是默认的调度器。
js
const observable = from([1, 2, 3]);
observable.pipe(
observeOn(queueScheduler)) // 使用队列调度器
.subscribe(value => console.log(value));
使用 queueScheduler
,操作会按顺序被放入队列,然后在下一个事件循环中执行。
-
asapScheduler
(asap 调度器):使用
asapScheduler
时,操作会在当前任务队列结束时(在当前宏任务结束前)尽快执行,这比queueScheduler
更快。
js
import { asapScheduler, asyncScheduler, from } from 'rxjs';
function syncSchedulerMain() {
console.log('before');
from([1, 2, 3]).subscribe(console.log)
console.log('after');
}
syncSchedulerMain();
// 执行结果:
// before
// 1
// 2
// 3
// after
使用 asapScheduler
,操作会在当前任务队列结束时(在当前宏任务结束前)尽快执行。
-
asyncScheduler
(异步调度器):使用
asyncScheduler
时,操作会被安排在一个微任务队列中,在当前任务执行完成后立即执行。这比asapScheduler
更快。
js
function asyncSchedulerMain() {
console.log('asyncScheduler: before');
from([1, 2], asyncScheduler).subscribe(console.log)
Promise.resolve('asyncScheduler: promise').then(console.log);
console.log('asyncScheduler: after');
}
// 执行结果:
// asapScheduler: before
// asapScheduler: after
// 1
// 2
// asapScheduler: promise
使用 asyncScheduler
,操作会在微任务队列中被安排,在当前任务执行完成后立即执行。
-
animationFrameScheduler
(动画帧调度器):使用
animationFrameScheduler
时,操作会在浏览器的下一个动画帧中执行,通常用于与动画相关的场景。
调度器可以通过在 Observable 上使用 observeOn
操作符来指定
js
const observable = from([1, 2, 3]);
observable.pipe(
observeOn(animationFrameScheduler)) // 使用动画帧调度器
.subscribe(value => console.log(value));
使用 animationFrameScheduler
,操作会在浏览器的下一个动画帧中执行,通常用于与动画相关的场景。