RxJS 是Reactive Extensions For JavaScript的简写,它是一个强大的 JavaScript Reactive 编程库。Reactive 是指响应式编程(Reactive Programming)。
中文官方介绍文档:概览 | RxJS 中文文档
1、什么是响应式编程(Reactive Programming)
任何异步事件(比如页面鼠标 click 事件),在响应式编程都是异步事件流。不仅仅是 click、hover 这种事件,任何变量、用户输入、属性、缓存、数据结构等,响应式编程把所有事物都看成是数据流。数据流是类似数组一样的序列,可以像数组一样,用 merge、map、concat 等方法操作。简单来说就是:把所有事物都事件流化,然后把这些事件流像数组一样去操作,就是响应式编程。
RxJS 提供了一个核心类型 Observable,附属类型 (Observer、 Schedulers、 Subjects) 和操作符 (map、filter、reduce、every, 等等),这些操作符可以把异步事件作为流(Observable)来处理。
2、使用RxJS的好处
(1)减少变量声明
以按钮点击事件为例,为统计按钮点击次数。通常的写法:
/**
* 按钮点击计数
*/
count: number = 0;
/**
* 绑定按钮点击事件
*/
bindBtn1CLick() {
const button = document.querySelector('#btn1') as HTMLElement;
button.addEventListener('click', () => console.log(`按钮1点击了 ${++this.count} 次`));
}
而使用 RxJS 的话,除了事件本身,不需要使用全局范围内额外的变量:
import { fromEvent } from 'rxjs';
import { scan } from 'rxjs/operators';
/**
* 绑定按钮点击事件
*/
bindBtn2CLick() {
const button = document.querySelector('#btn2') as HTMLElement;
fromEvent(button, 'click').pipe(
scan(count => count + 1, 0)
).subscribe(count => {
console.log(`按钮2点击了 ${count} 次`);
});
}
scan 操作符 scan(accumulator: (acc: T, value: T, index: number) => T, seed?: T) 工作原理与数组的reduce方法类似, 提供了一个初始值 seed, 每次回调函数运行后的返回值会作为下次回调函数运行时的参数。
(2)数据流更清晰
下面的代码展示的是如何控制一秒钟内最多点击一次,先来看使用普通的 JavaScript:
/**
* 按钮点击计数
*/
count: number = 0;
/**
* 时间间隔
*/
timeSpan: number = 1000;
/**
* 上一次点击时间
*/
lastClick: number = Date.now() - this.timeSpan;
/**
* 绑定按钮点击事件
*/
bindBtn1CLick() {
const button = document.querySelector('#btn1') as HTMLElement;
button.addEventListener('click', () => {
if (Date.now() - this.lastClick >= this.timeSpan) {
console.log(`按钮1点击了 ${++this.count} 次`);
this.lastClick = Date.now();
}
});
}
使用 RxJS:
import { fromEvent } from 'rxjs';
import { scan, throttleTime } from 'rxjs/operators';
/**
* 绑定按钮点击事件
*/
bindBtn2CLick() {
const button = document.querySelector('#btn2') as HTMLElement;
fromEvent(button, 'click').pipe(
throttleTime(1000),
scan(count => count + 1, 0)
).subscribe(count => {
console.log(`按钮2点击了 ${count} 次`);
});
}
可以看到代码简洁了很多,使用的全局变量也更少,而且通过 pipe 方法可以很清晰的通过代码看出数据流的处理流程:数据流产生后,先进行间隔1000毫秒的采样处理,再进行累加处理,最后才是最终的逻辑处理。
RxJS中对数据流产生数据的最终处理都是通过调用 subscribe() 方法进行处理,可以认为与一般JS中回调方法的概念类似。
3、RxJS in Angular
Angular从第一个版本起就集成了 RxJS,最常见的就是HttpClient发送异步请求时,方法返回的就是一个 Observable。
/**
* Constructs a request which interprets the body as a JSON object and returns
* the full `HttpResponse` with the response body in the requested type.
*
* @param method The HTTP method.
* @param url The endpoint URL.
* @param options The HTTP options to send with the request.
*
* @return An `Observable` of the full `HttpResponse`, with the response body of type `R`.
*/
request<R>(method: string, url: string, options: {
body?: any;
headers?: HttpHeaders | {
[header: string]: string | string[];
};
context?: HttpContext;
reportProgress?: boolean;
observe: 'response';
params?: HttpParams | {
[param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>;
};
responseType?: 'json';
withCredentials?: boolean;
}): Observable<HttpResponse<R>>;
本篇使用示例是直接在Angular项目中添加的演示代码,与官网文档的JS写法有少许差别。
当然响应式编程不仅仅是在 JS 里存在,它还支持各种语言,比如:RxJava、Rx.NET、RxPY、RxGo 等等。