本文已参与「新人创作礼」活动,一起开启掘金创作之路。
承接前文提到的异步Action话题,中间件redux-thunk易于编写和理解,但它也存在实践上的不足:
- 你最终可能会回调地狱,特别是在发出API请求时,
- 需要使用业务逻辑填充回调函数 或reducer函数去整理数据(数据格式不总是完美的,特别是使用第三方API时)
- 不利于测试(必须使用Spy方法来检查是否使用正确的对象进行dispatch)
然后现存的异步状态管理方案也有很多Saga什么的,所以……没什么所以,反正就是要讲Redux-Observable!然而又不得不先从RxJS开始:
RxJS
RxJS 是观察者模式的一个实现。它允许我们以声明方式组合 "Observables" 和 "Subjects" 的运算符来扩展观察者模式。
它被用于流式事件处理,能够更方便地梳理和管控异步逻辑。
它的核心概念,在本文中涉及Observables、Observers、Operators 和 Subjects 4个;
Observables 可观察对象; Observers 观察者; Operators 操作符; Subjects 主体(多播的Observable)
可观察对象Observable
可观察对象Observable 是可以在一段时间内 发出数据/产生事件 的对象。我们可以通过new来创建可观察对象(也可以使用一些 Operators 来创建),new时需要一个 入参为subscriber对象的函数 来定义 “如何产生事件”。
Observable 可使用 subscriber 的.next方法 将数据推送给观察者。
如果 Observable 成功完成,它可以使用该.complete方法通知观察者。
如果 Observable 遇到错误,它可以使用该.error方法将错误推送给观察者。
观察者可以按需多次调用.next,并在最后调用.complete 或.error。在 complete 或 error 之后,Observable 对象就不会向流中推送任何值了。
观察者Observers
其实前文也可以看出,Observable 的观察者(Observer)是具有三个函数的对象:next, error 和 complete,这些函数是可选的,它们也可以分别作为三个参数传给 Observable 对象身上的 subscribe 函数,让观察者对该 Observable 对象进行订阅。
Observer 定义了"如何响应事件",而Observable 定义了"如何产生事件";
graph RL
A(Observer<br>观察者) -- .subscribe --> B(Observable<br>可观察对象)
B --推送数据-->A
所以终究还是"订阅发布模式",你也可以说他是一个观察者模式
graph RL
C(订阅者) -- 订阅 --> D(发布者)
D --推送-->C
注:Observables 是"单播"的: 当 Observer 订阅一个 Observable 时,它会得到一份拥有自己执行路径的 Observable 的副本,从而使 Observables 单播。如果而后有新的 Observer 来订阅,那么会为它创建新的 Observable 副本从头观察事件的发生(旧的 Observer 不受影响 可继续运行)。
从此也可以看出Observable是可以被多次subscribe的; 此处通过静态定义数据源创建的可观察对象被称为 Cold Observable,而通过Promise、Event等产生的与观察者无关的数据源,即可创建出 Hot Observable,即便有多个观察者也共享同个事件流,可实现多播。
多播的Subjects
按理说 Subjects 是与 Observable 同等级别的角色,但它既能扮演 Observable 又能扮演 Observer。
它既可以被.subscribe()订阅,它又遵循观察者的结构:有.next .error和.complete方法。于是 Subject 对象也可作为Observer被传给e Observable 对象或其他 Subject对象的.subscribe()函数中。
似乎这样做只能使它能起到中转的作用,但实际上它相对 Observable 是"有状态的",可以记住数据已经发送多少了或者说哪些observer订阅了自己,那么它就能实现多播可观察对象的效果了:
graph LR
A(Cold Observable) --> B(Subject)-->C(observer 1)
B-->D(observer 2)
B-->E(observer...)
对于Cold Observable来说,Subject是一个observer;而对于下游的多个observer来说,Subject是一个多播的可观察对象。
任意一个observer调用subscriber.complete(),Subject就会终止发信,那么其他observer也会收不到数据(但可以收到complete通知,此时上游相当于EMPTY);
Subject还可以作为多个Observable的下游,形成一种数据流汇聚合并的效果;但是与上文相似地,当其中一个Observable结束时调用subscriber.complete(),也就结束了Subject,其他的Observable的数据此后就会接收不到,因此想要合并数据流还是不要用这种方式,而是使用merge操作符。
操作符Operators
两个主要类别是构建类和管道类
- Creation Operators: 构建一个新的 Observable
- Pipeable Operators: 接收一个 Observable 修饰改造后返回新的 Observable
操作符太多了,如有需要请参考: Rxjs知识整理 www.jianshu.com/p/16be96d69… 《深入浅出RxJS》
直接看一个例子:
// 构建类操作符interval产生了一个Cold Observable。每隔一秒发送自增1数字。
var source = Rx.Observable.interval(1000);
// 管道类操作符filter将可观察对象source转换为新的可观察对象newest。仅当数字为偶数时才可通过管道。
var newest = source.filter(x => x % 2 === 0);
newest.subscribe(console.log);
// 0 -> 2 -> 4 -> 6 -> ...
再挑几个说说吧
import { EMPTY, from, of } from 'rxjs';
EMPTY:创建一个立即结束的Observable。
from:从一个数组、类数组、Promise、迭代器对象或者类 Observable 对象创建一个 Observable;几乎可以把任何东西都能转化为Observable。
of:创建简单的 Observable ,只是同步地发出给定的参数, 在发送完这些参数后发出完成通知。(那是不是说form大部分能接受的参数...展开一下就能放到of里面了?)
import { switchMap, mergeMap, takeWhile, catchError } from 'rxjs/operators';
前两个需要区分一下
mergeMap:将每个源值project成 Observable ,该 Observable 会合并到输出的 Observable 中。
switchMap:将每个源值project成 Observable,该 Observable 会合并到输出的 Observable 中, 并且只发出最新投射的 Observable 中的值。
takeWhile:只有满足给定的条件时才接收源 Observable 的值,第一个不满足条件的值出现时,它便完成。
catchError:就是定义错误处理回调用的嘛。