前言
这里简单解释一下Rxjs,RxJS 是一个js库,它通过使用 observable 序列来编写异步和基于事件的程序。ReactiveX 结合了 观察者模式、迭代器模式 和 使用集合的函数式编程,以满足以一种理想方式来管理事件序列所需要的一切。看到这你肯定疑问它有什么用?你先放下这个疑问,先看看一个简单的案例。
Observable可观察对象
Observable可观察对象:表示一个可调用的未来值或者事件的集合。
一个例子
通常你这样注册事件监听:
var button = document.querySelector('button');
button.addEventListener('click', () => console.log('Clicked!'));
使用RxJS创建一个可观察对象:
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.subscribe(() => console.log('Clicked!'));
看到这段代码你可能迷茫,这是什么意思?难道只是换了一种写法?
观察者模式
要说Observable可观察对象首先得说说:观察者模式。
观察者模式又叫发布-订阅(Publish/Subscribe)模式:
他定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(也可叫做抽象的通知者)。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。而且各个观察者之间相互独立。
观察者模式的结构中包含四种角色:
(1)抽象主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法。
(2)抽象观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
(3)具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
(4)具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。
观察者模式结构的类图如下所示:
在现实生活中,我们经常用它来“放风”,比如:上自习时,老师不在我们在玩,派出一个同学看老师,老师来了通知大家;如果该同学没有发现老师,老师“咳咳”两声通知大家自己来了让大家安静自习,然后批评一番。
这里的监听的抽象主题对象是“老师是否来了”,同学们是观察者,同学们依赖主题对象的状态并且是一种一对多的依赖关系,同学们同时监听主题对象的反馈结果,同学们订阅(观察)这个主题,在这个主题发生变化时,来更新自己:
老师来了->安静自习,写作业。
老师没来->该玩玩,该吃吃。
放风的同学发现老师会通知,放风的同学没发现,老师进入教室老师自己也会“咳咳”两声通知。因此“老师是否来了”这个抽象主题中,老师通知和放风的同学通知都是这个抽象主题对象的具体实现,这个抽象主题对象就是可观察对象(可以观察嘛~~~),这时再想想前面对于可观察对象的定义(可调用的未来值或者事件的集合)是不是明白了?
同样上面的RxJS的代码也是这种效应,Rx.Observable.fromEvent(button, ‘click’)是一个创建一个点击事件的可观察对象,然后使用subscribe(() => console.log(‘Clicked!’));订阅这个可观察对象,其中() => console.log(‘Clicked!’)是一个观察者,如果发生点击事件主题对象的状态会发生改变,而他则会被执行。
这样有什么好处呢?
我们只需要针对可观察对象这一抽象的主题对象接口编程,减少了与具体的耦合,即他只是一个抽象的通知者。
观察者只依赖主题对象的状态,这意味着维持各个观察者的一致性,但又保证了各个观察者是相互独立的。
发布-订阅
Observables(可观察对象)以惰性的方式推送多值的集合。
示例 - 当订阅下面代码中的 Observable 的时候会立即(同步地)推送值1、2、3,然后1秒后会推送值4,再然后是完成流(即完成推送):
var observable = Rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
});
要调用 Observable 并看到这些值,我们需要订阅 Observable:
console.log('just before subscribe');
observable.subscribe({
next: x => console.log('got value ' + x),
error: err => console.error('something wrong occurred: ' + err),
complete: () => console.log('done'),
});
console.log('just after subscribe');
结果如下:
just before subscribe
got value 1
got value 2
got value 3
just after subscribe
got value 4
done
拉取 (Pull) vs. 推送 (Push)
拉取和推送是两种不同的协议,用来描述数据生产者 (Producer)如何与数据消费者 (Consumer)如何进行通信的。
什么是拉取? - 在拉取体系中,数据的消费者决定何时从数据生产者那里获取数据,而数据生产者自身并不会意识到什么时候数据将会被发送给数据消费者。
每个 JavaScript 函数都是拉取体系。函数是数据的生产者,调用该函数的代码通过从函数调用中“取出”一个单个返回值来对该函数进行消费(return 语句)。
ES2015 引入了生成器generator 函数 和 迭代器iterators (function*),这是另外一种类型的拉取体系。调用iterator.next()的代码是消费者,它会从 iterator(生产者) 那“取出”多个值。
什么是推送? -在推体系中,数据的生产者决定何时发送数据给消费者,消费者不会在接收数据之前意识到它将要接收这个数据。
Promise(承诺)是当今JS中最常见的Push推体系,一个Promise(数据的生产者)发送一个resolved value(成功状态的值)来注册一个回调(数据消费者),但是不同于函数的地方的是:Promise决定着何时数据才被推送至这个回调函数。
RxJS引入了Observables(可观察对象),一个新的 JavaScript 推送体系。一个可观察对象是一个产生多值的生产者,并将值“推送”给观察者(消费者)。
Function 是惰性的评估运算,调用时会同步地返回一个单一值。
Generator(生成器):是惰性的评估运算,在迭代时同步的返回零到无限多个值(如果有可能的话)
Promise 是最终可能(或可能不)返回单个值的运算。
Observable 是惰性的评估运算,它可以从它被调用的时刻起同步或异步地返回零到(有可能的)无限多个值。
|--------| producer(生产者) | consumer (消费者) | 单个值 | 多个值 |
| pull拉 | Passive(被动的一方):被请求的时候产生数据 | Active(起主导的一方):决定何时请求数据 | Function | Iterator|
| push推 | Active:按自己的节奏生产数据 | Passive:对接收的数据做出反应(处理接收到的数据) | Promise | Observable |
可观察对象(Observables):作为函数的泛化
与常见的主张相悖的是,可观察对象不像EventEmitters(事件驱动),也不象Promises因为它可以返回多个值。可观察对象可能会在某些情况下有点像EventEmitters(事件驱动),也即是当它们使用Subjects被多播时,但是大多数情况下,并不像EventEmitters。
可观察对象(Observables)像是没有参数, 但可以泛化为允许返回多个值的函数。
思考下面的程序