什么是RxJS【Reactive Extensions for JavaScript】
首先RxJS是一个库,是针对编程工具,当然Angular引入RxJS就是让异步更加简单,更加可控,在开始RxJS之前,我们先来了解一下Reactive Programming,其本质就是使用的一种编程方式。
什么是流呢?
首先看示例:
例子:有变量 a和b, b 永远等于 a + 1
// 基础写法
let a = 1;
b = a + 1;
console.log(b)
a = 2;
b = a + 1; // 每次a变化,都要执行一次b = a+ 1
console.log(b)
// 使用流来解决:
// 用 stream 描述 a 与 b 的关系。
const a$ = new Subject();
const b$ = a$.pipe(map( x=> x + 1));
b$.subscribe(console.log);
a$.next(1);
a$.next(2);
虽然在这里我们还不认识Subject是个什么,但是通过这个例子我们可以了解到,所谓流/stream,就是数据基于事件(event)变化的整体。,只要a变化,那么b就会自动跟着变化。
流的最大的好处是,能够监听数据的变化,执行相应的操作,从而最大可能性的减小性能的开销。对于rxjs而言任何东西都可以是streams
RxJS中的核心概念(Observable 、Observer 、Subscription、Subject)
在Angular项目中我们在调用接口的时候,常用的调用方式是:
this._exampleService.reportCurrentOpenTab$
.subscribe((res: number) => {
this.currentIndex = res;
})
//this._exampleService.reportCurrentOpenTab$ 这是返回observable,他可以是api的调用,可以是事件的调用等等
我们可以把上述的调用方式抽象一下为 在这里我们认识到了两个新的事物分别是Observable和Observer,以及这个方法调用的返回对象,返回的是一个Subscription对象的实例化,接下来我们逐一介绍这些核心概念。
Observable
Observable是RxJS中最核心的一个概念,它的本质就是“Observable is a function to generate values”,首先它是一个,也就是说它是,一般我们会在变量末尾加$表示Observable类型的对象。
// 此函数定义了setInterval 每两秒产生一个 value的功能
const observable$ = (observer) => {
let counter = 0;
const id = setInterval(() => observer.next(counter++), 2000);
}
// 因为Observable是个对象,所以需要调用才可以执行
observable$({ next: (val) => console.log(val) });
函数中会定义 value 的生成方式,函数调用时,observer.next 来执行在observer 中定义的行为,比如上述示例中的counter++。
- 必须调用(订阅)才会被执行
- observable 被调用后,必须能被关闭,否则会一只运行下去
- 对于同一个 observable,在不同的地方 subscribe,是无关的。function 执行多次,互相没有关联是一致的。
Observer
它是。它是一个有三个回调函数的,每个回调函数对应三种Observable发送的通知类型(next, error, complete),observer表示的是对序列结果的。在实际开发中,如果我们提供了作为参数,subscribe会将我们提供的函数参数作为的回调处理函数。next决定传递一个什么样的数据给观察者
let observer = {
next: data => console.log('data'); // next表示数据正常流动,
error: err=> console.log('err'); // error表示流中出错
complete: () => console.log('complete') // complete表示流结束
}
// error和complete只会触发一个,但是可以有多个next
Observables 与 Observer 之间的订阅发布关系(观察者模式) 如下:
订阅 :Observer 通过 Observable 提供的 subscribe() 方法订阅 Observable。
发布 :Observable 通过回调 Next 方法向 Observer 发布事件。
Subscription
:是一个Subscription对象。在 observable.subscribe 内部,他会创建一个观察者对象并使用第一个回调函数作为next的处理方法。Subscription就是表示Observable 的执行,可以被清理。这个对象最常用的方法就是 方法。同时,它还有 方法也可以使我们取消多个订阅,所以我们在Angular项目中会看到如下代码,去取消订阅:
// 定义Subscription类型的数组同于存放页面中的observable订阅过程中产生的Subscription
public subscriptions: Subscription[] = [];
public ngOnInit(): void {
this.subscriptions.push( // 将Subscription push到定义的数组中去
this._layoutStatusService.reportCurOpenTab$
.subscribe((res: number) => {
this.currentIndex = res;
})
);
}
// 在Angular的销毁生命周期中进行取消订阅,已达到优化性能的作用
public ngOnDestroy(): void {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
}
我们可以用生活中 “订阅报刊” 的例子,再来理解一下订阅关系:
报刊生产商【observable】
市民【observer】
市民打电话给报刊生产商进行订阅【subscribe】,市民就会在报刊商送报纸(next)成功的时候收到报纸,这个时候市民是不用关心报纸是怎么生产和运送的,当然当报刊生产商可能会意外的导致送报纸失败(error),而等成功之后,报刊生产商还是会把这次送报标记为已送达(complete)
Subject
Subject是:我们可以像订阅任何observable一样去订阅subject。
Subject是: 它有next(v),error(e),和complete()方法,如果我们需要给subject提供新值,只要调用,它会将值多播给已注册监听该subject的观察者。
所以: Subject既是Observable,也是观察者(可以多个)
Subject与Observable的区别:
- Subject是【他可以将值多播给多个观察者】
- 普通的Observble是【每个已经订阅的观察者(observer)都拥有observable的独立执行,上述Observble的介绍也有提及】
Subject中常用的子类(ReplaySubject、AsyncSubject、BehaviorSubject)
这里针对Subject的这三个衍生子类,只做简单的介绍,在后续开发过程中如果需要使用该方法,请自行查阅官网,进行进一步的深入了解。
BehaviorSubject: 会保存发送数据,当被订阅的时,会立即使用这个最新【最先的】数据,然后会继续接收新的next的值。BehaviorSubject必须设置默认值,因为他有一个最新值(当前值)的概念。
ReplaySubject:会保存所有值,然后回放给最新的订阅者,当新的订阅发生的时候,会把上一次订阅的都再次打印一遍~
AsyncSubject:只有当Observable执行完成时,它才会执行的发送给观察者,就是说会保留流里最后一条数据,而且只会在数据流complete时候才会发送。
Subject的在Angular中的常见的作用:
可以在Angular通过service来实现不同组件,或者不同模块之间的传值
// 定义公共的用于数据存储的service,文件名是(eg:xampleStore.service.ts)
@Injectable()
export class ExampleStoreService {
private currentTabNumber$ = new Subject();
}
// 此数据更改的逻辑,可以在任何需要更改的地方进行next相对应的值,文件名是 (eg:a.component.ts)
this.ExampleStoreService.currentTabNumber$.next(1);
// 订阅接收到数据更改,并做下一步逻辑处理,文件名是(eg:b.component.ts)
this.ExampleStoreService.currentTabNumber$
.subscribe((res: number) => {
this.currentIndex = res;
})
RxJS的操作符(Operator)简介
operators是个纯函数,它的输入为observable,返回也observable。operators的本质是,描述从一个数据流到另一个数据流之间的关系,也就是observer到observable中间发生的转换,很类似于Lodash。
在RxJS中操作符有接近100个,不过在开发过程稿常用的也就十多个。
常见的运算符包含 map,filter,concat,flatmap,switchmap,forkjoin
在这里我们只调挑出forkjoin来讲解一下,其他的操作符可以自己去查阅使用
forkjoin主要是用于多个接口都返回的时候,才会返回结果,有点类似于promise中的promise.all,具体的用法如下:
forkJoin([
this._aService.getData(),
this._bService.getData()
]).subscribe(resArr => {
// 此时的返回结果会被按顺序放在一个数组中
aData = resArr[0];
bData = resArr[1];
}
Observable与Promise的比较:
observable.subscribe(function next(value) {});
promise.then(function resolve(value) {});
从方法的调用上感觉这两种处理异步的形式是相似的,实际上observable比promise强大很多。具体对比如下:
Observable:
- RxJS 里面用的是 next() 和 subscribe()
- next 会被调用多次,可以发射多个值
- 但是observable在没有 subscribe 的情况下,是不是被执行的。
- RxJS unsubscribe可以取消订阅
- RxJS还提供了大量的操作符,可以方便出来各种流的情况
Promise:
- Promise里面用的是 then() 和 resolve()
- resolve 只会被调用一次,不能多次触发异步调用,
- then并不会触发promise的执行。也就是说,一个 promise不管有没有then, 都会被执行。
Observable与Promise的转换
RxJS是有提供方法,用来进行observable与promise之间转换的,所以在其他使用promise的项目里完全可以使用RxJS去迭代开发。
const promise = observableFun$.toPromise() // 将observable转换成promise
const observable = observable.fromPromise(promiseFun) // 将promise类型函数转换成observable
通过一个搜索示例,再来回顾一下今天介绍的RxJS
普通方式:
使用流的方式:
var inputControl = document.querySelector('#inputControl');
var inputStream = fromEvent(inputControl, 'keyup')
.pipe(
.debounceTime(300) // 防抖动
.pluck('target', 'value') // 使用pluck操作符,获取输入的值
.switchMap(url => Http.get(url)) // 将当前输入流替换为http请求
.subscribe(data => render(data)); // 接收数据
总结
上述介绍的内容几乎都是Rxjs中很基础的东西,RxJS的入门确实有些难度,但是如果掌握了之后,你会发现RxJS这种响应式编程是真的香,本篇文章中并没有着重介绍RxJS中的操作符,如果想了解更多关于操作符可以参考RxJS的操作符介绍。