rxjs合并数据流操作符

一、concat首尾相连

工作方式:

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

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

import { of,concat } from 'rxjs';
...  
  const source1$ = of(1,2,3)
  const source2$ = of(4,5,6)

  const source$ = concat(source1$,source2$)

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

输出结果

 

 二、merge:先到先得快速通过

工作方式:

第一个Observable对象不完结,并不影响下游的observable对象,随先得到,就先输出。

当所有的Observable对象完结了,merge才会完结自己的Observable对象。

import { timer, merge } from 'rxjs';
import 'rxjs/add/operator/map'

...
  // 从第0毫秒开始,每隔1000毫秒产生一个数据,依次是:0A、1A、2A....
  const source1$ =  timer(0,1000).map( x=> x + 'A')

 // 从第500毫秒开始,每隔1000毫秒产生一个数据,依次是:0B、1B、2B... const source2$
= timer(500,1000).map( x=> x+'B') const source$ = merge(source1$,source2$) source$.subscribe( console.log, null, ()=>console.log('complete') ) ...

输出结果前几个数值如下:

 

 同步限流

merge有一个可选参数,放在最后。表示指定可以同时合并的observable对象个数。

如果前面两个observable对象没有完结,第三个Observable对象永远不会进入source$。

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

  const source$ = merge(source1$,source2$,source3$,2)

merge的应用场景

fromEvent可以从网页中获取事件,但是fromEVent一次只能从一个DOM元素获取一种类型的事件。

如果某个元素的click事件,也同时需要touchend事件,因为移动设备上touchend事件出现得比click更早。

这两个事件的处理是一模一样的。

此时需要借助merge的力量。

const source1$ =  fromEvent(document.querySelector('#text'),'click')
const source2$ = fromEvent(document.querySelector('#text'),'touchend')

const source$ = merge(source1$,source2$)

结论:

merge只对产生异步数据的Observable才有意义。

 三、zip:拉链式组合

工作方式:

一对一合并,如果source1$吐出数据更快,也会等着source2$数据吐出后一对一配对输出。

即使source1$还有多余的数据,如果没有配对数据,将会执行complete完结。

因此会造成数据挤压问题。

zip也可以多个数据流合并。

  import { of, zip } from 'rxjs';
const source1$ =  of(1,2,3,4,5)
const source2$ = of('a','b','c')
const source$ = zip(source1$,source2$)

输出结果:

 四、combineLatest合并最后一个数据

实例1:

import { timer, combineLatest } from 'rxjs';
// 从第500毫秒开始,每隔1000毫秒产生数据
const source1$ =  timer(500,1000)
// 从第1000毫秒开始,每隔1000毫秒产生数据 const source2$
= timer(1000,1000) const combineLatest$ = combineLatest(source1$,source2$)

输出结果:(前几个结果)

 

弹珠图:

 

 

实例2:

    const source1$ =  timer(500,1000)
    const source2$ = of('a')

    const combineLatest$ = combineLatest(source1$,source2$)

执行结果:(前几个结果)

 

 实例3:

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

    const combineLatest$ = combineLatest(source1$,source2$)

执行结果:

 

 解析:

这是由combineLatest的⼯作⽅式决定的。
combineLatest会顺序订阅所有上游的Observable对象,
只有所有上游Observable对象都已经吐出数据了,
才会给下游传递所有上游“最新数据”组合的数据。在上⾯的例⼦中,
 
combineLatest的⼯作步骤如下:
  1)combineLatest订阅source1$,不过,因为source1$是由of产⽣的同步数据流,在被订阅时就会吐出所有数据,最后⼀个吐出的数据是字符串c。
  2)combineLatest订阅source2$。
  3)source2$开始吐出数据,当吐出1时,和source1$的最后⼀个数据c组合传给下游。
  4)source2$吐出2时,依然和source1$的最后⼀个数据c组合传给下游。
  5)source2$吐出3时,还是和source1$的最后⼀个数据c组合传给下游。

 实例4:

    const source1$ =  of('a','b','c')
    const source2$ = of(1,2,3)
    const source3$ = of('x','y')

    const combineLatest$ = combineLatest(source1$,source2$,source3$)

 

 执行结果:

 

 实例5:定制下游数据

zip和combineLatest一样默认输出的数据时数组形式,因此zip也和combineLatest一样默认输出的数据时数组形式,

同样也可以利用最后一个函数参数来定制输出数据的形式。

    const source1$ =  timer(500,1000)
    const source2$ = timer(1000,1000)
    const project = (a,b) => `${a} and ${b}`

    const combineLatest$ = combineLatest(source1$,source2$,project)

执行结果:

 五、withLatestFrom

工作方式:

withLatestFrom产生数据,只是更新两者的最新数据,

如果两者其中一个不是最新数据,也不会产生新的数据。

实例1:

import { timer } from 'rxjs';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/withLatestFrom'

    const source1$ =  timer(0,2000).map( x=> 100 * x )
    const source2$ = timer(500,1000)
    const project = (a,b) => a + b

    const combineLatest$ = source1$.withLatestFrom(source2$,project)

执行结果:(前几个)

 

 弹珠图如下所示:

 六、解决glitch问题

实例1:

import { timer } from 'rxjs';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/withLatestFrom'

    const original$ =  timer(0,1000)
    const source1$ = original$.map( x=> x+'a')
    const source2$ = original$.map( x=> x+'b')

    const combineLatest$ = source1$.withLatestFrom(source2$)

执行结果:(前几个)

 

 实例2:

import { combineLatest, fromEvent } from 'rxjs';
import 'rxjs/add/operator/map'

    const event$ = fromEvent(document.body,'click');
    const x$ = event$.map(e => e.x);
    const y$ = event$.map(e => e.y);
    const project = (x,y) => `x:${x},y:${y}`;
    const result$ = combineLatest(x$,y$,project);

    result$.subscribe(
      (location) => {
        console.log('#render', location)
        document.querySelector('#text').innerHTML = location;
      }
    )

当点击三次会出现以下结果:

 

 但是如果用withLatestFrom就可以解决以上问题:

减少不必要的网页渲染。

const result$ = x$.withLatestFrom(x$,y$,project);

当点击三次出现以下结果:

 七、race:胜者通吃

谁先吐出第一个数据为胜者,合并产生的数据就是胜者吐出的数据。

import { race, timer } from 'rxjs';
import 'rxjs/add/operator/map'
 
   const source1$ = timer(0,2000).map(e => e + 'a');
    const source2$ = timer(500,1000).map(e => e + 'b');
    const result$ = race(source1$,source2$)

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

执行结果:

 

 

弹珠图

 

八、startWith:先吐出指定的若干个数据

实例1:

import { timer } from 'rxjs';
import 'rxjs/add/operator/startWith'

    const orginal$ = timer(0,1000)
    const result$ =  orginal$.startWith('start')

执行结果:

 

 实例2: 

同样的效果,可以用concat来实现

    const orginal$ = timer(0,1000)
    const result$ = concat(of('start'),orginal$)

差异:

startWith的一点不足时所有参数都是同步吐出的,如果需要异步吐出参数,那还是只能利用concat。

 九、forkJoin:合并所有参数Observable对象的最后一个数据

实例:

import { interval, forkJoin } from 'rxjs';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/take'    

    const source1$ = interval(1000).map( x=> x+'a' ).take(1);
    const source2$ = interval(1000).map( x => x +'b').take(3);
    const concated$ = forkJoin(source1$,source2$)

执行结果:

 

posted @ 2020-03-04 10:31  芙蓉0504  阅读(948)  评论(0编辑  收藏  举报