RxJS——合并数据流

介绍:

        在RxJS的世界中,为了满足复杂的需求,往往需要把不同来源的数据汇聚在一起,把来自多个Observable对象的数据合并到一个Observable对象中,这就是合并操作符。

        这类操作符都有多个Observable对象作为数据来源,把不同来源的数据根据不同的规则合并到一个Observable对象中。

下面的示例代码均是以rxjs6版本写的。

1. concat:首尾相连

各种语言中都支持名为concat方法。我们可以看下在JavaScript中,数组中的concat方法,能够把多个数组中的元素依次合并到一个数组中。代码如下:

const arr1 = [1,2,3]
const arr2 = [4,5,6]
arr1.concat.(arr2) // [1, 2, 3, 4, 5, 6]

上面的demo,在RxJS中可以有两种实现方式,既可以通过实例操作符方式,也可以通过静态操作符方式。

使用实例操作符的方式。代码如下:

import { of } from 'rxjs'
import { concat } from 'rxjs/operators'

const source1$ = of(1,2,3)
const source2$ = of(4,5,6)
const concated$ = source1$.concat(source2$)

使用静态操作符的方式。代码如下:

import { of, concat } from 'rxjs'

const source1$ = of(1,2,3)
const source2$ = of(4,5,6)
const concated$ = concat(source1$, source2$)

从上面的代码可以看出,两种不同的实现方式,导入的语句是不同的。

对于实例操作符,第一个Observable就是调用concat的那个对象;对于静态操作符,第一个Observable是concat第一个参数。

当第一个Ovservable对象complete之后,concat就会去subscribe第二个Observable对象获取数据,把数据同样传给下游。

以此类推,直到最后一个Observable完结之后,concat产生的Observable也就完结了。

2. merge:先到先得快速通过

merge同concat类似,同时有静态和实例两种操作符,同样可以支持两个以上的Observable对象合并,用法相似,但功能完全不同。

(1)数据汇流,示例代码如下:

import { timer, merge } from 'rxjs'
import { map } from 'rxjs/operators'

const source1$ = timer(0, 1000).map(x => x+'A')
const source2$ = timer(500, 1000).map(x => x+'B')
const merged$ = merge(source1$, source2$)

merged$.subscribe(
    console.log,
    null,
    () => console.log('complete')
)

上面的代码会每隔500毫秒输出一行,永不停歇,输出的前几行如下:

0A

0B

1A

1B……

与concat不同的是,merge会第一时间订阅所有的上游Observable,然后对上游的数据采取“先到先得”的策略,任何一个Observable只要有数据推下来,就立刻转给下游Observable对象。

source1$从第0毫秒开始,每隔1000毫秒产生一个数据,依次是0A、1A、2A……

source2$从第500毫秒开始,每隔1000毫秒产生一个数据,依次是0B、1B、2B……

通过merge组合之后,就可以看到上面的数据交叉出现。

(2)应用场景

从创建数据流中我们知道,fromEvent可以从网页中获取事件,只可惜,fromEvent一次只能从一个DOM元素获取一种类型的事件。当我们需要同时关心某个元素的click事件,也关心这个元素上的touchend事件,因为在移动设备上touchend事件出现得比click更早,这两个事件的处理是一模一样的,但是fromEvent不同同时获得两个事件的数据流,这个时候就需要借助merge的力量。代码如下:

import { fromEvent, merge } from 'rxjs'

 
const click$ = fromEvent(element, 'click')
const touchend$ = fromEvent(element, 'touchend')
merge(click$, touchend$).subscribe(eventHandler)

3. zip拉链式组合

zip的含义就是“拉链”,这个操作符的名字非常直观,拉链的合并就是一对一的合并。

第一个同步数据流示例。代码如下:

import { of, zip } from 'rxjs'

const source1$ = of(1,2,3)
const source2$ = of(a,b,c)
const zipped$ = zip(source1$, source2$)

zipped$.subscibe(
    console.log,
    null,
    () => console.log('complete')
)

运行结果如下:

[1, 'a']

[2, 'b']

[3, 'c']

complete

从结果可以看到,在产生数据的形式上,zip会把上游的数据转化为数组形式,每一个上游Observable共享的数据会在对应数组中占有一席之地。

当zip执行的时候,它会立刻订阅所有的上游Observable,然后开始合并数据,在demo中,source1$产生的数据序列会和source2$产生的数据序列配对,1配上a,2配上b,3配上c,所以产生3个数组传递给下游。数据流订阅完结输出complete。

第二个异步数据流示例,代码如下:

import { of, interval } from 'rxjs'

const source1$ = interval(1000)
const source2$ = of('a', 'b', 'c')

const zipped$ = zip(source1$, source2$)

zipped$.subscibe(
    console.log,
    null,
    () => console.log('complete')
)

运行结果与上面一样。

这里需要注意,source1$是由interval产生的数据流,是永不完结的流,但在zip产生的Observable对象却在source2$吐完所有数据之后调用了complete。也就是说,只要任何一个上游的Observable完结,zip就会给下游一个complete信号。

当然这样肯定会产生数据积压问题,对于数据量比较小的Observable对象,这样的数据积压还可以忍受,但是对于超大量的数据流,使用zip就不得不考虑潜在的内存压力问题,zip这个操作符自身是解决不了这个问题的。

zip同concat、merge一样,是可以支持多个上游Observable对象的,只要做到一对一对一的咬合,就不难理解如何用zip来组合两个以上的数据流了。

参数书籍:《深入浅出RxJS》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值