RxJs高阶运算——concatMap

时间:2022-06-01 12:44:07

在我们的实际使用中,最重用的一些高阶映射运算主要有四类——concatMap,mergeMap,switchMap,exhaustMap

这些高阶映射运算符在网络响应式编程中的使用非常广泛,因此理解它们很重要。但是知道在什么场景下使用这些运算符本身就十分困惑,本文就是为了解疑释惑而写,我们后续会在本节的基础上分别理解这些高阶运算以及实际的场景。

为什么这些高阶映射运算会困惑?

首先需要理解的是每个高阶运算内部Observable的组合策略

举个例子,想要理解concatMap,首先需要理解的就是concat操作;想深入理解switchMap,首先得知道什么是switch

 

什么是高阶Observable映射?

我们知道,正常的map映射都是从一个普通的值类型映射到另一种值类型;而高阶的映射就是从一种普通的值类型映射到Observable类型,映射的结果是高阶Observable,它的特点是本身是Observable,但是它发出的值也是Observable,它可以接受订阅。

 

什么是Concatenation

RxJs高阶运算——concatMap

我们首先学习什么是concat。

RxJs高阶运算——concatMap

代码举例

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运算符

RxJs高阶运算——concatMap

代码举例

this.form.valueChanges
    .pipe(
        concatMap(formValue => this.http.put(`/api/course/${courseId}`, 
                                             formValue))
    )
    .subscribe(
       saveResult =>  ... handle successful save ...,
        err => ... handle save error ...      
    );

日志分析

RxJs高阶运算——concatMap

我们可以看到,这种方法的一个好处是避免了嵌套订阅,同时表单的值也是序列化的发送到后台。

正如我们所见,一个保存 HTTP 请求仅在前一个保存完成后才开始。以下是 concatMap 运算符如何确保请求始终按顺序发生:

1、concatMap 获取每个表单值并将其转换为保存的 HTTP Observable,称为内部Observable

2、concatMap 然后订阅内部 Observable 并将其输出发送到结果 Observable

3、第二个表单值可能比在后端保存前一个表单值更快

4、如果发生这种情况,新的表单值将不会立即映射到 HTTP 请求

5、相反,concatMap 将等待先前的 HTTP Observable 完成,然后再将新值映射到 HTTP Observable,订阅它并因此触发下一次保存

请注意,此处的代码只是保存草稿表单值的实现的基础。 您可以将其与其他运算符结合使用,例如仅保存有效的表单值,并限制保存以确保它们不会过于频繁地发生。理解了原理,一切尽在掌握之中。