Markdown测试


title: RxJS简介
date: 2017-08-01 09:45:33
tags: [JavaScript, RxJS]


摘要

RxJS是ReactiveX的JavaScript实现,除此之外还有RxJava、Rx.Net、RxSwift等,ReactiveX提供了一系列的数据流组合和控制能力。RxJS被广泛的运用在Angular中,本文做一些简单的资料搬运和介绍。

核心概念

Reactive Programming: 一种面向数据流(Data Flows)和变化传播(the Propagation of Change)的编程范式。
ReactiveX(Reactive Extensions): 简写Rx,由微软开发维护的基于Reactive Programming实现的一个工具库集合,Rx系列集合了观察者模式、迭代器模式和函数式编程,是响应式编程的优秀实践。

面向变化传播的示例与解释:

对下面语句
<pre><code>a = b + c</code></pre>
Imperative Programming: 一个赋值过程,赋值完成即结束,a和b、c的变化再没有关联。
Reactive Programming: 当b或者c发生变化时,a也会跟着发生变化。

在我们熟悉的MVVM中,也存在Model => View, View => Model这样的变化关系,当View有数据绑定时,View Model会更新Model,当Model变化时,Model会通知View Model、View Model进而通知View更新。前端的MVVM框架也体现了面向变化传播的思想。

面向数据流的示例与解释:

监听一系列的事件流,比如用户触发的事件、网络请求的响应、定时器、MutationObserver等等等等,我们通过RxJs的一系列方法将这一系列事件流转化为数据流,对数据流进行进行过滤、合并等各种你需要的操作后响应事件流的回调,这个过程就属于面向数据流的编程。

Observable

Observable将异步数据转化为数据流,转化后的Observable对象相当于数据源,我们后面的操作都在Observable对象的基础上进行,Observable是观察者模型中的被观察者。
{% codeblock lang:js %}
const button = document.querySelector('#js-button');
const buttonClick$ = Rx.Observable.fromEvent(button, 'click');
buttonClick$.subscribe(() => console.log('clicked'));
{% endcodeblock %}

Operator

Observable对象的操作者,可以对Observable对象进行过滤、合并、转化等操作,大多数operator会在操作完成后返回一个新的Observable对象供下一个operator进行处理,方便的进行链式调用。
{% codeblock lang:js %}
const myObservable = observable$.debounceTime(500).first()
{% endcodeblock %}

Observer

观察者模型中的观察者,当Observable被subscribe函数订阅时,subscribe期待一个observer来进行响应。
{% codeblock lang:js %}
observable$.subscribe(observer)
{% endcodeblock %}

Subscription

Observable被subscribe函数订阅时后返回的Subscription实例, 订阅对象有一个重要的方法:unsubscribe,用来释放资源或者取消Overvable对象的执行。
{% codeblock lang:js %}
const observable$ = Rx.Observable.interval(1000);
const subscription = observable$.subscribe(x => console.log(x));
subscription.unsubscribe();
{% endcodeblock %}
可以对单个订阅对象unsubscribe, 也可以对多个订阅对象unsubscribe,方法是将一个订阅对象add到另一个订阅中(remove方法从订阅对象中移出add进的订阅对象)

{% codeblock lang:js %}
const observable1$ = Rx.Observable.interval(200);
const observable2$ = Rx.Observable.interval(300);
const subscription1 = observable1$.subscribe(x => console.log(observable1:${x}));
const subscription2 = observable2$.subscribe(x => console.log(observable2:${x}));
subscription1.add(subscription2);
// 取消全部订阅
setTimeout(() => {
subscription1.unsubscribe();
}, 1000);
{% endcodeblock %}

Subject

Subject是一种特殊的Observable,它允许值被多个观察者执行,类似于EventEmitter的数据结构。它既是被观察者也是观察者。

作为被观察者多次被订阅

{% codeblock lang:js %}
const subject = new Rx.Subject();
subject.subscribe({
next: (value) => console.log(observerA:${value});
});
subject.subscribe({
next: (value) => console.log(observerB:${value});
});
subject.next(1);
subject.next(2);

// 打印结果
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2
{% endcodeblock %}

作为观察者成为subscribe的参数

{% codeblock lang:js %}
const subject = new Rx.Subject();
subject.subscribe({
next: (value) => console.log(observerA:${value});
});
subject.subscribe({
next: (value) => console.log(observerB:${value});
});
const observable$ = Rx.Observable.from([1, 2, 3]);
// subject作为观察者
observable$.subscribe(subject);

// 打印结果
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2
// observerA: 3
// observerB: 3
{% endcodeblock %}

Scheduler

调度者,控制着何时启动一个订阅和何时通知被发送。
Scheduler有三个组件构成:

  • 一个调度者是一个数据结构。它知道如何根据优先级或其他标准存储和排列任务。
  • 一个调度者是一个执行上下文。它表示何处何时任务被执行(例如: immediately(立即), or in another callback mechanism(回调机制), such as setTimeout(定时器) or process.nextTick(在下一个事件轮询节点执行), or the animation frame(动画))。
  • 一个调度者具有虚拟的时钟。它通过调度器上的getter方法now()提供了“时间”的概念。 在特定调度程序上调度的任务将仅仅遵守由该时钟表示的时间。

这是一个使用observeOn操作符传递这些值的异步调度程序
{% codeblock lang:js %}
const observable$ = Rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
})
.observeOn(Rx.Scheduler.async);
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
// just after subscribe
// got value 1
// got value 2
// got value 3
// done
{% endcodeblock %}

Observable的“冷”与“热”

Rx中,Observable通常实现成Hot和Cold模式,Hot模式下,Observable对象一旦创建就开始发送数据,在Cold模式下,Observable只有在被订阅后才会开始发送数据,RxJs实现的是Observable的“冷”模式。

示例代码:
{% codeblock lang:js %}
const observable$ = new Observable(observable => {
console.log('Observable Start!');
observable.next();
})
console.log('start');
observable$.subscribe();

// 打印结果
// start
// Observable Start!
{% endcodeblock %}

只有当observable被subscribe后才发送数据

Connectable模式

该模式下的Observable对象不管有没有被订阅,都不会发送数据,直到ConnectableObservable实例的conntect方法被调用。

示例代码:
{% codeblock lang:js %}
const observable$ = new Observable(observable => {
console.log('Observable Start!');
observable.next();
}).publish();

console.log('start');
observable$.subscribe();

console.log('after Observable has been subscribed');
observable$.connect()

// 打印结果
// start
// after Observable has been subscribed
// Observable Start!
{% endcodeblock %}

observable被publish方法转化为Connectable模式,它在observable$.connect()方法调用后才发送数据,而不是subscribe后立即开始发送。

在异步处理中对比Promise

Promise是目前我们最常用的异步解决方案,它是一种对异步操作的封装,一个好吃的语法糖,它定义了三个状态,pedding、fulfilled和rejected。它和RxJS的不同之处:

  1. Promise从pedding状态变为其它状态时是不可撤销的,而RxJS可以取消订阅。
  2. Observable可以持续emit多个值,而Promise只能emit一个值。
  3. Observable提供了许多的工具函数。

示例代码:

Promise:
{% codeblock lang:js %}
const promise = new Promise((resolve, reject) => {
if (//success) {
resolve(res);
} else {
reject(err);
}
});
promise
.then()
.then()
.catch()
{% endcodeblock %}

Observable:
{% codeblock lang:js %}
const observable$ = new Observable(observer => {
let interval = setInterval(() => {
let count = 0;
observer.next(count)
}, 1000);
return () => {
clearInterval(interval);
}
});
observable$
.map(value => value.toString())
.subscribe(value => console.log(value));
{% endcodeblock %}
总结: RxJs是Promise进阶版,提供了更多的功能和更精细的控制。

推荐阅读更多精彩内容

  • 一.背景介绍 Rx(Reactive Extension -- 响应式扩展 http://reactivex.io...
    爱上Shu的小刺猬阅读 1,601评论 1 3
  • 介绍 RxJS是一个异步编程的库,同时它通过observable序列来实现基于事件的编程。它提供了一个核心的类型:...
    泓荥阅读 14,857评论 0 11
  • 标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 注:# 和一级标题之间建议保留一个字符的空格,这...
    demo11阅读 212评论 0 0
  • 【七月影语】20170808学习力践行Day79 依然在医院和家里奔跑,晚上在小姐姐家给孩子读了故事,背了古诗《题...
    小手拉大手_4f23阅读 45评论 0 0
  • 如何成为一个高境界的备胎?能起到千斤顶的作用帮助更换其他备胎。
    迎刃阅读 854评论 0 3