写在前面
做相关整理时,RxJS还停留在5.0,如今RxJS已升级到6.0,建议大家在学习相关内容时直接选择最新版本:RxJS中文文档
概念
Rx(Reactive Extension,响应式扩展)确切讲是一种编程思想,最早是微软的开源类库,之后随着RxJava的出现才广受追捧起来。显然,RxJS是Rx的JavaScript的实现;
那么响应式扩展,或者说响应式编程,到底是怎样的?先引一下wiki:
在计算领域,响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
Emm..这都说的什么鬼?好吧,概念总是抽象的,那么我们看实例:
- 传统编程:
var a = 1;
var b = 2;
var c = a + b;
a = 2;
b = 3;
console.log(c);
// 3
// 没什么好说的,a和b的变化不会影响到c;
- 响应式编程
var a$ = Rx.Observable.from([1,2]);
var b$ = Rx.Observable.from([2,3]);
var c$ = Rx.Observable.zip(a$,b$,(a,b)=>{
return b + c;
})
c$.subscribe(c => console.log('c: ' + c));
// c: 3
// c: 5
// 可见:随着a,b值的变化,c的值也跟着发生变化
现在再回过头来看wiki,是不是能理解一些了?
借用一条经典结论:传统编程基于的是离散的点;而响应式编程基于的是连续的线
应用
讲完概念,接下来该讲讲RxJS具体怎么去用了。
操作符是RxJS重要成员,因此我们先从操作符说起:
Tips:以下代码可以直接在 https://jsbin.com/ 上运行
运行前需在body里插入 <script src="https://unpkg.com/@reactivex/...;></script>
-
创建类操作符
- from
var array = [10, 20, 30]; var result$ = Rx.Observable.from(array); result$.subscribe(x => console.log(x));
- fromEvent
var input = document.getElementById('input'); var input$ = Rx.Observable.fromEvent(input, 'keyup'); input$.subscribe(x => console.log(x.target.value));
- fromEventPattern
function addClickHandler(handler) { document.addEventListener('click', handler); } function removeClickHandler(handler) { document.removeEventListener('click', handler); } var click$ = Rx.Observable.fromEventPattern( addClickHandler, removeClickHandler ); click$.subscribe(x => console.log(x));
- interval
var resource$ = Rx.Observable.interval(500).take(3) resource$.subcribe(x => console.log("Next: " + x), err => console.log("Error: " + err), ()=> console.log('finished'));
- timer
var resource$ = Rx.Observable.timer(2000); resource$.subcribe(x => console.log("Next: " + x), err => console.log("Error: " + err), ()=> console.log('finished')); var resource2$ = Rx.Observable.timer(2000, 1000); resource2$.subcribe(x => console.log("Next: " + x), err => console.log("Error: " + err), ()=> console.log('finished'));
-
合并类操作符
- combineLatest
var click = document.getElementById('click'); var input = document.getElementById('input'); var click$ = Rx.Observable.fromEvent(click,'click').mapTo('clicked'); var input$ = Rx.Observable.fromEvent(input,'input').map(x => x.target.value); var result$ = Rx.Observable.combineLatest(click$,input$,(ev,input) => { return {ev: ev, input: input} }) result$.subscribe(x => console.log(x));
- zip
var click = document.getElementById('click'); var input = document.getElementById('input'); var click$ = Rx.Observable.fromEvent(click,'click').mapTo('clicked'); var input$ = Rx.Observable.fromEvent(input,'input').map(x => x.target.value); var result$ = Rx.Observable.zip(click$,input$,(ev,input) => { return {ev: ev, input: input} }) result$.subscribe(x => console.log(x)); // 试比较与combineLatest的不同 var a$ = Rx.Observable.from(['hello','world','hello','rxjs']); var b$ = Rx.Observable.interval(2000); var result$ = a$.zip(b$,(item,index) => {return {item: item, index: index}}) result$.subscribe(res => console.log('Index: ' + res.index + ', Item: ' + res.item)) // "Index: 0, Item: hello" // "Index: 1, Item: world" // "Index: 2, Item: hello" // "Index: 3, Item: rxjs"
- merge
var a$ = Rx.Observable.interval(2000).map(x => -x*10).take(5); var b$ = Rx.Observable.timer(0,2000).map(x => x*10).take(5); var result$ = Rx.Observable.merge(a$,b$) result$.subscribe(x => console.log(x));
- concat
var a$ = Rx.Observable.from([1,3,5,7,9]); var b$ = Rx.Observable.from([2,4,6,8,10]); var result$ = Rx.Observable.concat(a$,b$); result$.subscribe(x => console.log(x));
-
过滤类操作符
- filter
var a$ = Rx.Observable.range(0,10); var result$ = a$.filter(x => x%5 == 0 ); result$.subscribe(x => console.log(x)) // 0 // 5
- take (略,前面有演示了)
- debounce
var input = document.getElementById('input'); var input$ = Rx.Observable.fromEvent(input,'keyup').pluck('target','value'); input$.debounceTime(500).subscribe(x => console.log(x));
-
其他(上面例子中都用到了,自己感受下)
- pluck
- range
- mapTo
- map
说完运算符,再补充RxJS中另一个重要成员:Subject
Subject是一类特殊的Observable对象,它既是观察者(Observer),又是可观察对象(Observable),因此:
- 作为观察者,它订阅者可以发推送
- 作为可观察对象,它可以被订阅
let subject = new Rx.Subject();
subject.subscribe(value => console.log('observableA: ' + value));
subject.subscribe(value => console.log('observableB: ' + value));
subject.next(1);
subject.next(2);
// observableA: 1
// observableB: 1
// observableA: 2
// observableB: 2
- Subject同样是由next,error,complete这些方法构成的对象
- Subject是一个Observable对象,因此可以作为订阅普通Observable对象的参数
let subject = new Rx.Subject();
subject.subscribe(value => console.log('observableA: ' + value));
subject.subscribe(value => console.log('observableB: ' + value));
let observable = new Rx.Observable.from([1,2]);
observable.subscribe(subject);
// observableA: 1
// observableB: 1
// observableA: 2
// observableB: 2
Subject还有两个常用的衍生类
- BehaviorSubject
let subject = new Rx.BehaviorSubject(0);
// 此时,0作为初始值
subject.subscribe(value => console.log('observableA: ' + value));
subject.next(1);
subject.next(2);
subject.subscribe(value => console.log('observableB: ' + value));
subject.next(3);
// observableA: 0
// observableA: 1
// observableA: 2
// observableB: 2
// observableA: 3
// observableB: 3
- ReplaySubject
let subject = new Rx.ReplaySubject(3);
// 此时,3作为参数,表示保留最近的三个值
subject.subscribe(value => console.log('observableA: ' + value));
subject.next(1);
subject.next(2);
subject.subscribe(value => console.log('observableB: ' + value));
subject.next(3);
// observableA: 1
// observableA: 2
// observableB: 1
// observableB: 2
// observableA: 3
// observableB: 3
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。