RXJS小组有问题吗?

时间:2017-10-25 09:14:11

标签: operators rxjs

我需要对RxJS groupBy运算符执行某些操作,并尝试了解它是如何工作的。

从文档示例中:

const people = [{name: 'Sue', age:25},
{name: 'Joe', age: 30},
{name: 'Frank', age: 25}, 
{name: 'Sarah', age: 35}];

//Generate source from an array
const source = Rx.Observable.from(people);

//group by age
const example = source
.groupBy(person => person.age)
//accumulates
.mergeMap(group => group.reduce((acc, curr) => [...acc, ...curr], []))
.subscribe(console.log);

输出是:

[{name: "Sue", age: 25}, {name: "Frank", age: 25}]

[{name: "Joe", age: 30}]

[{name: "Sarah", age: 35}]

我需要一些更“反应”的东西,我的意思是,我得到一个无限的流发射值,我需要按时间缓冲,然后对结果进行分组。我的代码与前一代码相似,但不起作用

const subject = new Rx.Subject();

setInterval(() => {
  const rnd = Math.random();
  const person = rnd < 0.25 ? {name: 'Sue', age: 25} : (rnd > 0.25 && rnd < 0.50) ? {name: 'Joe', age: 30} : 'Sarah', age: 35};

//emits the generated value
subject.next(person);
}, 500);

subject.bufferTime(2000)
.mergeMap(x => Rx.Observable.from(x))
.groupBy(person => person.age)
.mergeMap(group => group.reduce((acc, curr) => [...acc, ...curr], []))
.subscribe(console.log);

正如您所看到的,只是生成代码不同,但我的订阅中没有任何内容。

这是jsbin链接:jsbin example

我做错了什么?

1 个答案:

答案 0 :(得分:1)

因为您正在使用mergeMap将基于时间的缓冲项内联到单个流中,所以流永远不会完成。因此,使用永远不会完成的groupBy创建组。因此,这些组将永远不会发射到mergeMap,从而将它全部缩减为单个数组。

要完成分组,您可以将.take(XX)附加到源流,但这不会导致您正在寻找的行为。然后,当原始流同时完成时,所有组都完成。使用RxFiddle.net非常容易找到它:

RxFiddle stream visualisation

这种方法的问题是:

  • 您的分组仅在流完成时完成
  • 第一个分组接收所有值,第二个分组接收来自该时间点的所有值等。

如何在每个窗口完成分组

为此,您必须对每个缓冲区/窗口值而不是主流进行分组。然后当groupBy完成.reduce()完成后,您的数组将被发出。

Rx.Observable.interval(500)
  .map(i => Math.random())
  .map(rnd => rnd < 0.25 
       ? {name: 'Sue', age: 25} 
       : (rnd > 0.25 && rnd < 0.50)
         ? {name: 'Joe', age: 30} 
         : {name: 'Sarah', age: 35}
      )
  .window(Rx.Observable.interval(2000))
  .take(3)
  .mergeMap(x => {
    return x
      .groupBy(person => person.age)
      .mergeMap(group => group.reduce((acc, curr) => [...acc, curr], []))
})
  .subscribe(val => console.log(val), err => console.log('err: ' + err));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.2.0/Rx.js"></script>

很少有其他注意事项:

  • 我已将您的示例Subject + setInterval替换为Rx等效Rx.Observable.interval +地图
  • window(windowBoundaries)相当于bufferTime,但会发出Observable<T>而不是[]<T>,因此您可以移除Rx.Observable.from([]<T>)
  • .reduce((acc, curr) => [...acc, curr], [])相当于RxJs中可用的.toArray()函数