前言
上文中讲了Subscription
与Subscriber
,而在整个Rxjs
的体系中observer
为观察者,Subscription
充当清理管理器,Subscriber
是清理管理器也是观察者,而Observable
是扮演一个什么角色呢?你会发现这些都缺乏一个源头,观察谁?而Observable
充当的就是可观察对象(数据生产者)。
Observable
如何创建Observable
1. 基础操作符
of
:将参数转换为 Observable 序列
const observable1 = of(1, 2, 3);
from
: 将数组、Promise 等转换为 Observable
const observable2 = from([1, 2, 3]);
fromEvent
: 将事件转换为 Observable
const clickObservable = fromEvent(document, 'click');
2. 自定义的方式
// 创建自定义的 Observable
const customObservable = new Observable<number>(subscriber => {
// 设置定时器
const intervalId = setInterval(() => {
subscriber.next(Math.random());
}, 1000);
// 清理函数
return () => {
clearInterval(intervalId);
};
});
关于Observable
既然已经会创建Observable
了,那么我们在详细的聊一聊Observable
与前面几者的关系。
举个例子
Observable
就是主播(数据生产者)
Observer
就是观众(数据消费者)
Subscriber
是带有管理权限的观众(可以接收内容,也可以管理资源)
Subscription
是退出机制的管理(管理清理工作)
那整体的代码可能就如下
// 创建一个直播间(Observable)
const liveStream = new Observable<string>(subscriber => {
// 主播开始产生内容
subscriber.next('欢迎来到直播间!');
const timer = setInterval(() => {
subscriber.next('直播内容...');
}, 1000);
// 提供清理机制(关闭直播)
return () => {
clearInterval(timer);
console.log('直播结束,清理资源');
};
});
// 观众(Observer)进入直播间
liveStream.subscribe({
next: content => console.log('观众收到:', content),
error: err => console.log('直播出错:', err),
complete: () => console.log('直播结束')
});
那么从这段代码中可以知道Observable的职责
- 产生数据
- 决定何时发送数据
- 决定何时结束
- 提供清理机制
本质
- 定义数据流
// Observable 本身只是定义了数据流的蓝图
const observable = new Observable<number>(subscribe => {
// 这里的代码并不会立即执行
// 只是定义了当有订阅发生时要做什么
});
- 提供订阅机制
class Observable<T> {
// 核心是提供 subscribe 方法
subscribe(observer?: Partial<Observer<T>>): Subscription {
const subscriber = new Subscriber(observer);
subscriber.add(this._subscribe(subscriber));
return subscriber;
}
}
这边其实也发现了subscribe
的本质是充当了一个桥梁,它连接了Observable
和 Subscriber
。
- 支持清理逻辑的定义
const observable = new Observable(() => {
// 返回清理逻辑
return () => {
console.log('清理');
};
});
Observable和Subscriber相结合诞生的特点
Observable
承担了数据生产者的角色,而Subscriber
通过subscribe
作为订阅者,接收数据、处理错误或完成通知。两者结合因此诞生了以下的特点
懒执行
const observable = new Observable(subscriber => {
console.log('Observable 执行了');
subscriber.next(1);
});
// 这时候不会打印任何东西
observable.subscribe(value => console.log(value));
// 这时才会执行并打印: 'Observable 执行了' 和 1
可取消
const subscription = observable.subscribe(value => console.log(value));
// 稍后可以取消订阅
subscription.unsubscribe();
单播
const observable = new Observable(subscriber => {
const random = Math.random();
subscriber.next(random);
});
// 每个订阅者获得不同的值
observable.subscribe(value => console.log('订阅者1:', value));
observable.subscribe(value => console.log('订阅者2:', value));
小结
最初一切从Subscription(清理管理器)
开始,而Subscriber
继承于Subscription
并且实现了Observer
,所以它具有清理管理器+数据处理的功能。
而数据的生产者Observable
内部提供了一个桥梁subscribe
来连接Observable
与Subscriber
的关系.
Subject
最后我们聊一下Subject
,这个来说常见很多。先观察一下他的内部结构
class Subject<T> extends Observable<T> implements SubscriptionLike {
// 属性
observers: Observer<T>[]; // 存储观察者数组
closed: boolean; // 是否关闭
isStopped: boolean; // 是否停止
hasError: boolean; // 是否有错误
thrownError: any; // 错误信息
// 方法
next(value?: T): void; // 发送值
error(err: any): void; // 发送错误
complete(): void; // 发送完成
unsubscribe(): void; // 取消订阅
}
可以发现他是继承于Observable
具有发送数据的功能, 并且具有observers
属性(数组),这就是意味着它具有向多个Observable
发送数据的功能,也就是多播。
多播
当多个观察者订阅了Subject
时,在Subject
发送数据后,观察者就能收到数据,例如以下代码
const subject = new Subject<number>();
// 多个观察者会收到相同的值
subject.subscribe(value => console.log('观察者1:', value));
subject.subscribe(value => console.log('观察者2:', value));
subject.next(1);
// 输出:
// 观察者1: 1
// 观察者2: 1
结合多播的特点往往会拥有以下的作用
事件总线(Event bus)
class EventBus {
private subject = new Subject<any>();
emit(event: any) {
this.subject.next(event);
}
on(callback: (event: any) => void) {
return this.subject.subscribe(callback);
}
}
const eventBus = new EventBus();
eventBus.on(event => console.log('收到事件:', event));
eventBus.emit('hello');
例如:登录场景下,成功登录后需要将相应信息告诉别的画面比如用户信息,那么在登录成功后触发emit
函数,在用户信息画面通过on
来接收。
状态管理
import { BehaviorSubject, Observable } from 'rxjs';
class AuthStore {
// 私有化Subject防止外部直接操作
private _userState = new BehaviorSubject<{ isLoggedIn: boolean }>({ isLoggedIn: false });
// 对外暴露Observable接口
public get userState$(): Observable<{ isLoggedIn: boolean }> {
return this._userState.asObservable();
}
// 受控的状态修改入口
updateLoginStatus(isLoggedIn: boolean): void {
this._userState.next({ isLoggedIn });
}
}
// 使用示例
const authStore = new AuthStore();
// 订阅状态变化(组件层)
authStore.userState$.subscribe(state => {
console.log('登录状态变更:', state.isLoggedIn);
});
// 修改状态(服务层)
authStore.updateLoginStatus(true);
其实状态管理与事件总线的实现方式很像,但这里说的是另一个小知识点,当使用Subject/BehaviorSubject
作为状态管理时,通常会遇到使用Subject
的asObservable
方法将Subject
转为Observable
,这样的目的是保证数据源的访问权限,防止外部直接调用next()
方法修改状态,确保状态变更的可控性
Subject的兄弟
谈到Subject
必然少不了它的几个兄弟,接下来一一说明。
BehaviorSubject:有初始值,保存最新值
BehaviorSubject
相较于Subject
来说,在实际的开发过程中可能更常使用。因为在定义状态管理器时往往是需要一个默认值的,但Subject
不具有这样的特点。
因此总结BehaviorSubject
的特点:
- 必须提供初始值,确保订阅者总能获得一个值,而使用
Subject
无法获取当前值 - 新订阅者可以立即获得最新值,不用等待下一次更新,而使用
Subject
新订阅者需要等待下一次数据更新才能显示内容
例如
import { Subject, BehaviorSubject } from 'rxjs';
// 普通 Subject 示例
const subject = new Subject<number>();
// 订阅者只能获得订阅之后发出的值
subject.subscribe(value => {
console.log('Subject subscriber 1:', value);
});
subject.next(1); // 输出: Subject subscriber 1: 1
// 后面的订阅者看不到之前的值
subject.subscribe(value => {
console.log('Subject subscriber 2:', value);
});
subject.next(2);
// 输出:
// Subject subscriber 1: 2
// Subject subscriber 2: 2
// BehaviorSubject 示例
const behaviorSubject = new BehaviorSubject<number>(0); // 需要提供初始值
// 第一个订阅者会立即获得当前值(0)
behaviorSubject.subscribe(value => {
console.log('BehaviorSubject subscriber 1:', value);
}); // 输出: BehaviorSubject subscriber 1: 0
behaviorSubject.next(1); // 输出: BehaviorSubject subscriber 1: 1
// 新订阅者会立即获得最后发出的值(1)
behaviorSubject.subscribe(value => {
console.log('BehaviorSubject subscriber 2:', value);
}); // 输出: BehaviorSubject subscriber 2: 1
behaviorSubject.next(2);
// 输出:
// BehaviorSubject subscriber 1: 2
// BehaviorSubject subscriber 2: 2
AsyncSubject:只发送最后一个值
const async = new AsyncSubject();
async.subscribe(x => console.log(x));
async.next(1);
async.next(2);
async.next(3);
async.complete(); // 只有在完成时才输出最后一个值: 3
ReplaySubject:可以回放指定数量的值
const replay = new ReplaySubject(2); // 保存最近2个值
replay.next(1);
replay.next(2);
replay.next(3);
replay.subscribe(x => console.log(x)); // 输出: 2, 3
总结
到这里关于RxJS
告一段落,整理了关于Subscription
、Subscriber
、Subscribe
、Observer
、Observable
以及Subject
这些常见的概念。主要是将这些知识点关联起来形成自己的图谱加深印象,并且在实际工作中使用以此加强理解。