在我们的实际使用中,最重用的一些高阶映射运算主要有四类——concatMap
,mergeMap
,switchMap
,exhaustMap
这些高阶映射运算符在网络响应式编程中的使用非常广泛,因此理解它们很重要。但是知道在什么场景下使用这些运算符本身就十分困惑,本文就是为了解疑释惑而写,我们后续会在本节的基础上分别理解这些高阶运算以及实际的场景。
为什么这些高阶映射运算会困惑?
首先需要理解的是每个高阶运算内部Observable的组合策略。
举个例子,想要理解concatMap
,首先需要理解的就是concat操作;想深入理解switchMap
,首先得知道什么是switch
。
什么是高阶Observable映射?
我们知道,正常的map映射都是从一个普通的值类型映射到另一种值类型;而高阶的映射就是从一种普通的值类型映射到Observable类型,映射的结果是高阶Observable,它的特点是本身是Observable,但是它发出的值也是Observable,它可以接受订阅。
什么是Concatenation
我们首先学习什么是concat。
代码举例
const series1$ = of('a', 'b');
const series2$ = of('x', 'y');
const result$ = concat(series1$, series2$);
result$.subscribe(console.log);
日志信息:
a
b
x
y
日志分析:
我们用of创建了两个Observable,series1$,series2$,然后用concat操作符将series1$和series2$创作出result$。最后订阅输出。
我们理解一下这个操作符的弹珠图:
1、两个 Observables series1$ 和 series2$ 被传递给 concat() 函数
2、concat() 将订阅第一个 Observable series1$,但不订阅第二个 Observable series2$(重点重点)
3、source1$ 发出值 a,它立即反映在输出 result$ Observable 中
4、注意 source2$ Observable 还没有发出值,因为它还没有被订阅
5、source1$ 然后将发出 b 值,该值反映在输出中
6、source1$ 将完成,只有在此之后 concat() 现在订阅 source2$
7、source2$ 值将开始反映在输出中,直到 source2$ 完成
8、当 source2$ 完成时, result$ Observable 也将完成
9、请注意,我们可以将任意数量的 Observables 传递给 concat(),而不仅仅是本例中的两个
总结:cancat操作的关键是先订阅第一个Observable,当第一个发送完值以后,再订阅第二个,以此类推,一直到所有Observable都完成。
有了以上的基础知识,我们现在可以实现一个叫做序列化保存的小功能。所谓序列化保存就是当我们在前台向后台提交一系列保存后,保存操作会序列化完成,即下一个保存必须等到上一个保存完毕后才提交请求。想一想,这种需求我们以前是怎么做的。下面我们使用RxJs的concatMap来完成。这里我们用angular的form表单来举例子。
为了确保我们的表单值是按顺序保存的,我们需要获取每个表单值并将其映射到 httpPost$ Observable。然后我们需要订阅它,但我们希望在订阅下一个 httpPost$ Observable 之前完成保存。然后我们将订阅每个 httpPost$ 并按顺序处理每个请求的结果。最后,我们需要一个操作符来混合:
1、高阶映射操作(获取表单值并将其转换为 httpPost$ Observable)
2、使用 concat() 操作,将多个 httpPost$ Observable 连接在一起,以确保在上一个正在进行的保存首先完成之前不会进行 HTTP 保存
我们需要的是恰当命名的 RxJs concatMap Operator,它将高阶映射与 Observable 连接混合在一起。
RxJs ConcatMap运算符
代码举例
this.form.valueChanges
.pipe(
concatMap(formValue => this.http.put(`/api/course/${courseId}`,
formValue))
)
.subscribe(
saveResult => ... handle successful save ...,
err => ... handle save error ...
);
日志分析
我们可以看到,这种方法的一个好处是避免了嵌套订阅,同时表单的值也是序列化的发送到后台。
正如我们所见,一个保存 HTTP 请求仅在前一个保存完成后才开始。以下是 concatMap 运算符如何确保请求始终按顺序发生:
1、concatMap 获取每个表单值并将其转换为保存的 HTTP Observable,称为内部Observable
2、concatMap 然后订阅内部 Observable 并将其输出发送到结果 Observable
3、第二个表单值可能比在后端保存前一个表单值更快
4、如果发生这种情况,新的表单值将不会立即映射到 HTTP 请求
5、相反,concatMap 将等待先前的 HTTP Observable 完成,然后再将新值映射到 HTTP Observable,订阅它并因此触发下一次保存
请注意,此处的代码只是保存草稿表单值的实现的基础。 您可以将其与其他运算符结合使用,例如仅保存有效的表单值,并限制保存以确保它们不会过于频繁地发生。理解了原理,一切尽在掌握之中。