在Rxjs中,如何展平或合并包含普通类型和Observables的流?

时间:2016-05-29 06:55:56

标签: javascript rxjs reactivex

类似于数组flatten([1, 2 [3, 4], [5, 6]]) === [1, 2, 3, 4, 5, 6]

我想在rxjs observables中执行此操作:

const test$ = Rx.Observable.from(
    [1, 2, Rx.Observable.from([3, 4]), 5, Rx.Observable.from([6, 7])]
).mergeAll()

test$.subscribe(x => console.log(x)) //I want to output 1, 2, 3, 4, 5, 6, 7

mergeAll不起作用并抛出错误。

这是非常脏的解决方案:

const inElegant$ = Rx.Observable.merge(
  test$.filter(x => x instanceof Rx.Observable).mergeAll(),
  test$.filter(x => !(x instanceof Rx.Observable))
)

inElegant$.subscribe(x => console.log(x));

有没有更好的解决方案?

Jsbin http://jsbin.com/vohizoqiza/1/edit?js,console

3 个答案:

答案 0 :(得分:4)

如果我们在表单上有一个流

const stream = Rx.Observable.from(
    [1, 2, Rx.Observable.from([3, 4]), 5, Rx.Observable.from([6, 7]), 8])

有几种方法可以将其转换为纯数字流(不包括解决方案中的过滤。)

这是三种可能的解决方案:

// prints 1, 2, 3, 4, 5, 6, 7
stream
    .select(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .concatAll()
    .subscribe(x => console.log(x));

// prints 1, 2, 3, 4, 5, 6, 7
stream
    .selectMany(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .subscribe(x => console.log(x));

// prints 1, 2, 3, 4, 5, 6, 7
stream
    .select(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .mergeAll()
    .subscribe(x => console.log(x));

这一切看起来都不错。但是有几件事需要考虑。如果我们改变源流以使其异步:

const asyncStream = Rx.Observable.interval(1000)
    .select((val, idx) => idx + 8).take(5);

const stream = Rx.Observable.from(
    [1, 2, Rx.Observable.from([3, 4]), 5, Rx.Observable.from([6, 7]),
    asyncStream, 13, 14, 15])

我们使用与之前相同的解决方案获得以下结果:

// prints 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
stream
    .select(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .concatAll()
    .subscribe(x => console.log(x));

// prints 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 8, 9, 10, 11, 12
stream
    .selectMany(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .subscribe(x => console.log(x));

// prints 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 8, 9, 10, 11, 12
stream
    .select(e => typeof e == 'number' ? Rx.Observable.from([e]) : e)
    .mergeAll()
    .subscribe(x => console.log(x));

总结一下。使用selectManyselect后跟mergeAll解决了生成正确类型的展平列表的问题,但订单未得到维护。当任何流产生值时,这些解决方案将监听所有流并产生结果。

concatAll解决方案的行为略有不同。此解决方案将按顺序侦听每个流,仅在最后一个流完成时切换到下一个值/流。

所以这些是一些解决方案,您需要的解决方案取决于您的需求。然而,所有这些都不需要过滤流。

答案 1 :(得分:1)

我个人对这些案例使用toObservable转换功能。该函数使得observable保持不变,并将其他类型包装在一个observable中(使用Rx.Observable.return)。所以就像这样使用:

const test$ = Rx.Observable.from(
    [1, 2, Rx.Observable.from([3, 4]), 5, Rx.Observable.from([6, 7])]
).map(toObservable).mergeAll()

这与你正在做的很接近但是我发现将它打包成一个可以在其他环境中重用的单独函数很方便。

答案 2 :(得分:0)

根据documentation mergeAll

  

将可观察的可观察序列序列合并为可观察序列。

你有一个混合集合(数字和可观察量),所以不起作用。

这样做的唯一方法就是你如何做,用不同的方法处理这两种类型:虽然你应该问自己,同样的序列中是否有Observable和原始类型。