【Rxjs】 - 解析四种主题Subject

引言

开发ngx(angular 2+)应用时,基本上处处都会用到rxjs来处理异步请求,事件调用等等。因此常常会使用Subject来处理源源不断的数据流,好比input text change, toast notification等等。
这都要依赖于Subject自己既能够是Observable也能够是Observer,也就是说subject既能够做为一个数据源,也能够自己成为一组订阅者的代理
但当处理更加复杂的业务需求时,仅仅用Subject可能没法知足要求,这个时候就考虑一下rxjs提供的其余Subject Class了, 例如BehaviorSubject ReplaySubject AsyncSubject, 接下来咱们就来看一下他们跟Subject有什么区别,各自有什么特色,在何时更适合使用。typescript

[文中代码均使用typescript]异步

Subject

首先咱们来建立一个Rxjs Subject, 数据的类型是numberasync

let subject1: Subject<number> = new Subject<number>();  // (A)

而后咱们使用Subjectnext方法来emit(发射)1条数据spa

subject1.next(100); (B)

接下来对subject1建立两个订阅,在subscription中直接打印接受到的数据代理

subject1.subscribe((res: number) => console.info("subjectA ", res));  // (C)
 subject1.subscribe((res: number) => console.info("subjectB ", res));

接下来我在发射两条数据code

subject1.next(200); (D)
 subject1.next(300);

好了,接下来咱们就来看看console里面会打印出什么结果。
也许有的同窗会以为结果是这样的,由于Subject能够接收源源不断的数据嘛,因此不管发射多少次数据,订阅者都能接收到。server

//output
subjectA 100
subjectB 100
subjectA 200
subjectB 200
subjectA 300
subjectB 300

这个结果不太对
由于Subject的订阅者只有在订阅后,才能接收到数据源发射过来的值。
因此在代码块C中, 订阅者在订阅数据源subject1以前, 不管代码块B执行多少次, 订阅者也只能收到代码C以后发射的数据。blog

正确的结果应该是:rxjs

//output
subjectA 200
subjectB 200
subjectA 300
subjectB 300

这种状况在项目里常常能遇到,有时候我明明从数据源发射一个数据,但在订阅者拿到的值倒是undefined或者null, 这就是由于订阅者是在数据源发射以后建立的,天然没法接收到数据了。
假如咱们想在订阅者建立以后,不管何时都能拿到数据, 这应该怎么办呢? 那就要考虑使用BehaviourSubject了。事件

BehaviorSubject

咱们依旧使用刚才的例子, 建立一个BehaviorSubject, 默认值设为0. BehaviorSubject须要给个默认值
而后发射一条数据100,建立一个订阅者,再发射一条数据200,再建立一个订阅者,最后发射一条数据300。
代码以下:

let subject2: BehaviorSubject<number> = new BehaviorSubject<number>(0);
subject2.next(100);
subject2.subscribe((res: number) => console.info("behavior-subjectA ", res));
subject2.next(200);
subject2.subscribe((res: number) => console.info("behavior-subjectB ", res));
subject2.next(300);

这个时候结果就应该是:

//output
behavior-subjectA 100
behavior-subjectA 200
behavior-subjectB 200
behavior-subjectA 300
behavior-subjectB 300

因为BehaviorSubject是能够存储最后一条数据或者初始默认值的, 因此不管订阅者何时订阅到数据源subject2上, 都能接收到数据。
因此针对订阅者behavior-subjectA, 他订阅的时候,数据流里最后一条数据是100, 他能当即接收到。 而后依次能接收到最新的数据200300
针对订阅者behavior-subjectB, 他订阅的时候,数据流里最后一条数据是200, 他能当即接收到。 而后只能能接收到最新的数据300了。

BehaviorSubject给予咱们的便利就是,不管什么时候订阅到数据源,始终都能拿到最新的或者初始的数据,但也只能拿到一条数据,但当咱们处理input text change事件时,须要拿到用户输入的全部字符,也就是数据流的全部数据,BehaviorSubject就无能为力了,这个时候咱们考虑使用ReplaySubject了。

ReplaySubject

咱们依旧使用刚才的例子, 建立一个ReplaySubject, 发射两条数据100和200,建立一个订阅者,再发射一条数据300,再建立一个订阅者,最后发射一条数据400。
代码以下:

let subject3: ReplaySubject<number> = new ReplaySubject<number>();
subject3.next(100);
subject3.next(200);
subject3.subscribe((res: number) => console.info("replay-subjectA ", res));
subject3.next(300);
subject3.subscribe((res: number) => console.info("replay-subjectB ", res));
subject3.next(400);

控制打印的结果将是:

//output
replay-subjectA 100
replay-subjectA 200
replay-subjectA 300
replay-subjectB 100
replay-subjectB 200
replay-subjectB 300
replay-subjectA 400
replay-subjectB 400

ReplaySubject会存储数据流中的全部数据不管什么时候订阅到subject3,订阅者都能获取了订阅以前数据流里的全部数据,而后依旧获取到接下来获取的到的新数据。
就像ReplaySubject类名的中Replay, 一旦订阅到数据源,就会将数据流像放电影同样从新放一遍给你。

订阅者replay-subjectA订阅到subject3的时候,数据流里已经有了100和200, 接收并打印出来。
最后打印新数据300和400.
订阅者replay-subjectB订阅到subject3的时候,数据流里已经有了100,200,300, 接收并打印出来。最后打印新数据400.

接下来就要说下最后一种Subject了,也就是AsyncSubject

AsyncSubject

AsyncSubjectBehaviorSubject`ReplaySubject`有些相似,但不一样的是AsyncSubject只会存储数据流里的最后一条数据, 并且只有在数据流complete时才会将值发布出去
AsyncSubject主要是用来处理异步操做,当数据源是异步请求或者事件处理时,可能会发射出不少数据,若是咱们只但愿数据源的异步操做完成的时候,订阅者才能接收到值,这个时候就可使用AsyncSubject了。
接下来咱们看个例子,
建立一个AsyncSubject, 而后发射数据,建立订阅者,再发射数据。。。

let subject4: AsyncSubject<number> = new AsyncSubject<number>();
subject4.next(100);
subject4.next(100);
subject4.subscribe((res: number) => console.info("async-subjectA ", res));
subject4.next(300);
subject4.subscribe((res: number) => console.info("async-subjectB ", res));
subject4.next(400);
subject4.subscribe((res: number) => console.info("async-subjectC ", res));
subject4.complete();
subject4.subscribe((res: number) => console.info("async-subjectD ", res));
subject4.next(500);

最后的结果就应该是:

//output4
async-subjectA 400
async-subjectB 400
async-subjectC 400
async-subjectD 400

因为subject4AsyncSubject, 只有在complete的时候才会向订阅者publish数据,并且只publish最后一次数据,因此不管订阅者什么时候订阅数据源,均可以接收到最后一次数据。
但为何没有打印出500呢,由于数据源已经complete了,你就没法再发射新数据了。

总结

最后来总结一下四种Subject的特色,理解好的各自的特色,在项目开发中能够处理不少棘手的需求,同时也会避免不少问题的发生。
图片描述

相关文章
相关标签/搜索