文章目录
3.2 ReactX(Rx)概述
ReactX(简称Rx)是一套基于观察者模式的异步编程API,它结合了观察者模式、迭代器模式和函数式编程的最佳思想。最初由Microsoft开发,现已成为跨多种编程语言的响应式编程标准实现。
核心概念与历史背景
ReactX诞生于2010年左右,最初是微软.NET平台上的Reactive Extensions库,后来被移植到多种语言平台。其主要目标是:
- 简化异步编程
- 统一事件处理和数据流处理
- 提供强大的数据转换和组合能力
- 实现优雅的错误处理和资源管理
多语言实现
ReactX拥有多种语言的实现版本:
- RxJava:Java平台的实现,被广泛用于Android开发
- RxJS:JavaScript实现,用于前端和Node.js开发
- Rx.NET:.NET平台的原始实现
- RxSwift/RxCocoa:Apple生态系统实现
- RxKotlin:Kotlin语言的扩展实现
核心优势
- 函数式风格:使用声明式、不可变的方式处理数据流
- 组合性:操作符可以轻松组合形成复杂的数据处理管道
- 异步友好:内置对异步操作的支持,简化并发编程
- 错误处理:提供统一的错误处理机制
- 背压支持:部分实现支持响应式流的背压控制
3.2.1 RxJava / RxJS / Rx.NET 详解
RxJava
架构特点
RxJava是ReactX的Java实现,主要组件包括:
- Observable:数据流的源头
- Observer:数据流的消费者
- Scheduler:线程调度控制器
- Operator:数据流转换操作符
核心类与接口
// 基本使用示例
Observable.create(emitter -> {
// 发射数据
emitter.onNext("Data 1");
emitter.onNext("Data 2");
emitter.onComplete();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data -> System.out.println("收到: " + data),
error -> error.printStackTrace(),
() -> System.out.println("完成")
);
线程模型
RxJava通过Scheduler提供强大的线程控制:
- Schedulers.io():I/O密集型操作
- Schedulers.computation():计算密集型操作
- AndroidSchedulers.mainThread() (RxAndroid):Android主线程
- Schedulers.newThread():创建新线程
- Schedulers.single():单一线程顺序执行
背压支持
RxJava通过Flowable类提供背压支持:
Flowable.range(1, 1000000)
.onBackpressureBuffer(1000) // 缓冲策略
.observeOn(Schedulers.computation())
.subscribe(i -> {
// 慢速消费者
Thread.sleep(10);
System.out.println(i);
});
RxJS
前端应用特点
RxJS是JavaScript的ReactX实现,特别适合:
- 处理DOM事件
- AJAX请求管理
- 状态管理
- 动画序列控制
核心概念
// 基本使用示例
import {
fromEvent } from 'rxjs';
import {
throttleTime, map } from 'rxjs/operators';
const button = document.getElementById('myButton');
fromEvent(button, 'click')
.pipe(
throttleTime(1000),
map(event => event.clientX)
)
.subscribe(x => console.log(`点击位置X: ${
x}`));
操作符分类
- 创建操作符:of, from, fromEvent, interval
- 转换操作符:map, pluck, scan, buffer
- 过滤操作符:filter, take, skip, distinct
- 组合操作符:merge, concat, combineLatest, zip
- 错误处理:catchError, retry, retryWhen
Rx.NET
.NET生态系统集成
Rx.NET是ReactX的原始实现,深度集成于.NET平台:
- 与LINQ语法兼容
- 与async/await模式互操作
- 支持事件系统转换
典型应用场景
// 组合多个异步数据源
var observable1 = Observable.FromAsync(() => httpClient.GetAsync("url1"));
var observable2 = Observable.FromAsync(() => httpClient.GetAsync("url2"));
observable1.Zip(observable2, (res1, res2) => new {
res1, res2 })
.Subscribe(result => {
Console.WriteLine($"结果1: {
result.res1}, 结果2: {
result.res2}");
});
与Task的互操作
// Observable与Task转换
Observable.FromAsync(() => SomeAsyncMethod())
.Timeout(TimeSpan.FromSeconds(5))
.Subscribe(
result => Console.WriteLine(result),
ex => Console.WriteLine($"错误: {
ex.Message}")
);
// 反向转换
var task = observable.ToTask();
3.2.2 Observable(可观察对象)
概念与本质
Observable是ReactX的核心抽象,代表一个可观察的数据流,具有以下特性:
- 可推送多个值:随时间推移发出零个或多个值
- 惰性计算:只有被订阅时才开始执行
- 可完成或错误终止:流可以正常结束或以错误终止
- 可取消:订阅关系可以被取消
生命周期
- 创建阶段:通过create或工厂方法创建
- 订阅阶段:被Observer订阅时激活
- 执行阶段:开始发射数据项
- 终止阶段:完成或出错
创建方式
1. 工厂方法创建
// RxJava示例
Observable<String> simple = Observable.just("单一值");
Observable<Integer> range = Observable.range(1, 10);
Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS);
2. 从现有数据创建
// RxJS示例
const fromArray = rxjs.from([1, 2, 3]);
const fromPromise = rxjs.from(fetch('url'));
const fromEvent = rxjs.fromEvent(document, 'click');
3. 使用create方法
// Rx.NET示例
var observable = Observable.Create<int>(observer => {
observer.OnNext(1);
observer.OnNext(2);
observer.OnNext(3);
observer.OnCompleted();
return Disposable.Empty;
});
热Observable vs 冷Observable
冷Observable
- 每个订阅者获得独立的数据流
- 数据生产从订阅时开始
- 如HTTP请求、数据库查询
Observable<String> cold = Observable.fromCallable(() -> {
System.out.println("数据生产");
return "新数据";
});
cold.subscribe(System.out::println); // 触发数据生产
cold.subscribe(System.out::println); // 再次触发数据生产
热Observable
- 多个订阅者共享同一数据流
- 数据生产与订阅无关
- 如鼠标移动事件、股票价格更新
ConnectableObservable<String> hot = Observable.interval(1, SECONDS)
.map(i -> "数据"+i)
.publish();
hot.connect(); // 开始发射数据,与订阅无关
Thread.sleep(2500);
hot.subscribe(System.out::println); // 从当前数据开始接收
多播与共享
// RxJS多播示例
const source = interval(1000).pipe(
take(5),
share() // 共享同一订阅
);
source.subscribe(x => console.log(`观察者1: ${
x}`));
setTimeout(() => {
source.subscribe(x => console.log(`观察者2: ${
x}`));
}, 2000);
3.2.3 Observer(观察者)
接口定义
Observer是Observable的消费者,基本接口通常包含:
interface Observer<T> {
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}
订阅过程
- 创建Observer:定义处理逻辑
- 订阅Observable:建立关联关系
- 接收通知:处理数据、错误或完成信号
- 取消订阅:释放资源(可选)
完整示例
// RxJava Observer实现
Observer<String> observer = new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
// 可以存储Disposable用于后续取消
}
@Override
public void onNext(String s) {
System.out.println("收到: " + s);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("流结束");
}
};
Observable.just("数据1", "数据2", "数据3")
.subscribe(observer);
部分观察者
大多数Rx实现允许部分定义Observer:
// RxJS部分Observer
observable.subscribe(
value => console.log(value), // 只有next处理
error => console.error(error) // 可选的error处理
// 可选的complete处理
);
资源管理与取消订阅
显示取消
// Rx.NET取消订阅
IDisposable subscription = observable.Subscribe(
x => Console.WriteLine(x),
ex => Console.WriteLine(ex.Message),
() => Console.WriteLine("完成")
);
// 稍后取消
subscription.Dispose();
自动取消
// RxJava使用CompositeDisposable管理多个订阅
CompositeDisposable composite = new CompositeDisposable();
Disposable d1 = observable1.subscribe();
Disposable d2 = observable2.subscribe();
composite.addAll(d1, d2);
// 一次性取消所有
composite.clear();
3.2.4 Operators(操作符)
操作符概述
操作符是ReactX的强大之处,允许以声明式方式转换、组合和操作数据流。主要分类包括:
- 创建操作符:创建Observable
- 转换操作符:转换发射的值
- 过滤操作符:选择性地发射值
- 组合操作符:组合多个Observable
- 错误处理:处理错误情况
- 工具操作符:提供辅助功能
- 条件与布尔:条件判断
- 数学与聚合:数学运算
常用操作符详解
1. 创建操作符
// RxJS创建操作符示例
const created = new Observable(subscriber => {
subscriber.next('Hello');
subscriber.next('World');
subscriber.complete();
});
const fromEvent = fromEvent(document, 'click');
const interval = interval(1000);
const of = of(1, 2, 3);
const from = from([1, 2, 3]);
2. 转换操作符
// RxJava转换操作符
Observable.range(1, 5)
.map(i -> i * 2) // 每个值乘2
.flatMap(i -> Observable.just(i, i)) // 每个值复制一次
.scan((acc, curr) -> acc + curr) // 累加
.subscribe(System.out::println);
3. 过滤操作符
// Rx.NET过滤操作符
Observable.Interval(TimeSpan.FromSeconds(0.5))
.Take(10) // 取前10个
.Skip(3) // 跳过前3个
.Distinct() // 去重
.Where(x => x % 2 == 0) // 只取偶数
.Subscribe(Console.WriteLine);
4. 组合操作符
// RxJS组合操作符
const timer1 = interval(1000).pipe(take(5));
const timer2 = interval(2000).pipe(take(3));
merge(timer1, timer2) // 合并两个流
.subscribe(x => console.log(x));
zip(timer1, timer2) // 成对组合
.subscribe(([t1, t2]) => console.log(`${
t1}-${
t2}`));
5. 错误处理操作符
// RxJava错误处理
Observable.create(emitter -> {
try {
emitter.onNext(doSomethingRisky());
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
})
.retry(3) // 重试3次
.onErrorResumeNext(Observable.just("备用值"))
.subscribe(
System.out::println,
error -> System.out.println("最终错误: " + error)
);
自定义操作符
实现方式
// RxJava自定义操作符
public static <T> ObservableTransformer<T, T> addLogging() {
return upstream -> upstream
.doOnNext(item -> System.out.println("数据: " + item))
.doOnSubscribe(s -> System.out.println("订阅"))
.doOnComplete(() -> System.out.println("完成"));
}
Observable.range(1, 3)
.compose(addLogging())
.subscribe();
高级示例:缓冲超时操作符
// RxJS自定义缓冲超时操作符
function bufferTimeout<T>(timeout: number) {
return (source: Observable<T>) =>
new Observable<T[]>(observer => {
let buffer: T[] = [];
let timer: any;
const flush = () => {
if (buffer.length > 0) {
observer.next(buffer);
buffer = [];
}
};
const resetTimer = () => {
if (timer) clearTimeout(timer);
timer = setTimeout(flush, timeout);
};
return source.subscribe({
next(value) {
buffer.push(value);
resetTimer();
},
error(err) {
flush();
observer.error(err);
},
complete() {
flush();
observer.complete();
}
});
});
}
// 使用
fromEvent(document, 'click')
.pipe(bufferTimeout(1000))
.subscribe(clicks => console.log(`1秒内点击次数: ${
clicks.length}`));
操作符决策树
选择操作符的通用思路:
- 想创建Observable:使用创建操作符(just, from, create等)
- 想转换数据:使用转换操作符(map, flatMap, scan等)
- 想过滤数据:使用过滤操作符(filter, take, skip等)
- 想组合多个流:使用组合操作符(merge, zip, combineLatest等)
- 想处理错误:使用错误处理操作符(catch, retry等)
- 想控制时间:使用时间相关操作符(delay, throttle, buffer等)
ReactX高级主题
背压策略
背压问题场景
当生产者速度快于消费者时,需要背压控制策略:
- 无背压控制:导致内存溢出
- 简单丢弃:可能丢失重要数据
- 智能缓冲:平衡生产和消费速度
RxJava背压操作符
Flowable.range(1, 1000000)
.onBackpressureBuffer(1000) // 缓冲1000个项
.observeOn(Schedulers.computation())
.subscribe(i -> {
Thread.sleep(10); // 慢速消费者
System.out.println(i);
});
背压策略类型
- Buffer:缓冲所有来不及处理的数据
- Drop:丢弃来不及处理的数据
- Latest:只保留最新的数据
- Error:在溢出时抛出错误
测试Rx代码
使用TestScheduler
// RxJava测试示例
TestScheduler testScheduler = new TestScheduler();
Observable<Long> observable = Observable
.interval(1, TimeUnit.SECONDS, testScheduler)
.take(5);
TestObserver<Long> testObserver = observable.test();
testScheduler.advanceTimeBy(3, TimeUnit.SECONDS);
testObserver.assertValues(0L, 1L, 2L);
testScheduler.advanceTimeTo(5, TimeUnit.SECONDS);
testObserver.assertComplete();
虚拟时间测试
// RxJS虚拟时间测试
it('should test async operations', () => {
const scheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
scheduler.run(({
cold, expectObservable }) => {
const source = cold('--a--b--c--|');
const expected = '---a--b--c--|';
expectObservable(source).toBe(expected);
});
});
Rx与UI开发
响应式UI模式
// RxJS与UI交互
const searchInput = document.getElementById('search');
const searchResults = document.getElementById('results');
fromEvent(searchInput, 'input')
.pipe(
map(e => e.target.value),
debounceTime(300),
distinctUntilChanged(),
switchMap(query =>
from(fetch(`/api/search?q=${
query}`).then(res => res.json()))
)
)
.subscribe(results => {
searchResults.innerHTML = results
.map(r => `<li>${
r.name}</li>`)
.join('');
});
状态管理
// 使用RxJS实现简单状态管理
class Store<T> {
private stateSubject: BehaviorSubject<T>;
constructor(initialState: T) {
this.stateSubject = new BehaviorSubject(initialState);
}
get state$() {
return this.stateSubject.asObservable();
}
update(updater: (state: T) => T) {
this.stateSubject.next(updater(this.stateSubject.value));
}
}
const userStore = new Store({
name: '', loggedIn: false });
userStore.state$.subscribe(state => {
console.log('状态更新:', state);
});
userStore.update(state => ({
...state, loggedIn: true }));
ReactX最佳实践
编码规范
-
链式调用格式化:合理换行保持可读性
observable .map(...) .filter(...) .flatMap(...) .subscribe(...);
-
避免嵌套订阅:使用flatMap代替
// 不好 observable1.subscribe(value => { observable2(value).subscribe(...); }); // 好 observable1.pipe( flatMap(value => observable2(value)) ).subscribe(...);
-
合理命名Observable:使用$后缀约定
const user$ = getUserObservable(); const posts$ = getPostsObservable();
性能优化
-
选择合适的调度器:
- I/O操作使用Schedulers.io()
- 计算密集型使用Schedulers.computation()
-
合理使用共享:
Observable<Integer> shared = observable .doOnNext(...) .share(); // 多个订阅者共享同一执行
-
避免内存泄漏:
val disposables = CompositeDisposable() observable .subscribe(...) .addTo(disposables) // 集中管理Disposable
反模式与陷阱
-
忽略错误处理:
// 不好 - 忽略错误 observable.subscribe(value => ...); // 好 observable.subscribe({ next: value => ..., error: err => console.error(err) });
-
过度使用Subject:
- Subject功能强大但容易滥用
- 优先使用create或工厂方法创建Observable
-
忽略取消订阅:
Disposable disposable = observable.subscribe(...); // 在适当的时候取消 disposable.dispose();
ReactX与现代架构
MVVM模式中的应用
// RxSwift在MVVM中的典型应用
class ViewModel {
let searchText = PublishSubject<String>()
let results: Observable<[String]>
let isLoading: Observable<Bool>
init(api: SearchAPI) {
self.results = searchText
.debounce(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest {
query in
api.search(query)
.asObservable()
.catchErrorJustReturn([])
}
.share(replay: 1)
self.isLoading = Observable.merge(
searchText.map {
_ in true },
results.map {
_ in false }
)
}
}
与Redux的结合
// RxJS实现Redux风格状态管理
function createStore(reducer: (state: any, action: any) => any, initialState: any) {
const action$ = new Subject();
const state$ = action$.pipe(
startWith({
type: '@@INIT' }),
scan((state, action) => reducer(state, action), initialState),
shareReplay(1)
);
return {
dispatch: (action: any) => action$.next(action),
getState: () => {
let currentState;
state$.pipe(take(1)).subscribe(s => currentState = s);
return currentState;
},
state$
};
}
微服务中的响应式
// Spring WebFlux + RxJava
@RestController
public class UserController {
@GetMapping("/users")
public Flux<User> getUsers() {
return userRepository.findAll()
.timeout(Duration.ofSeconds(1))
.onErrorResume(e -> Flux.just(fallbackUser()));
}
@PostMapping("/users")
public Mono<Void> createUser(@RequestBody User user) {
return userRepository.save(user)
.then();
}
}
总结
ReactX通过Observable、Observer和丰富的操作符,提供了一套强大的异步编程模型。其核心价值在于:
- 统一的异步处理:用相同的方式处理各种异步数据源
- 声明式组合:通过操作符链式调用构建复杂逻辑
- 强大的错误处理:提供系统化的错误处理机制
- 背压支持:解决生产者-消费者速度不匹配问题
无论是前端交互、后端服务还是数据处理管道,ReactX都能显著提升代码的可读性、可维护性和扩展性。掌握ReactX编程模型,将使开发者能够更好地应对现代软件开发中的复杂异步场景。