RxJs разделения потока на несколько потоков

как я могу разделить бесконечный поток на несколько конечных потоков на основе метода группировки?

--a--a-a-a-a-b---b-b--b-c-c---c-c-d-d-d-e...>

в эти наблюдаемые

--a--a-a-a-a-|
             b---b-b--b-|
                        c-c---c-c-|
                                  d-d-d-|
                                        e...>

Как видите,a в начале, и после получения b, Я больше не будет a Так что это должно быть закончено. Вот почему нормальный groupBy не есть хорошо.

3 ответов


можно использовать window и share наблюдаемого источника. Есть также небольшой трюк с bufferCount(2, 1):

const str = 'a-a-a-a-a-b-b-b-b-c-c-c-c-d-d-d-e';
const source = Observable.from(str.split('-'), Rx.Scheduler.async).share();

source
    .bufferCount(2, 1) // delay emission by one item
    .map(arr => arr[0])
    .window(source
        .bufferCount(2, 1) // keep the previous and current item
        .filter(([oldValue, newValue]) => oldValue !== newValue)
    )
    .concatMap(obs => obs.toArray())
    .subscribe(console.log);

это печатает (из-за toArray()):

[ 'a', 'a', 'a', 'a', 'a' ]
[ 'b', 'b', 'b', 'b' ]
[ 'c', 'c', 'c', 'c' ]
[ 'd', 'd', 'd' ]
[ 'e' ]

проблема с этим решением заключается в порядке подписки на source. Нам нужно window notifier подписаться перед первым bufferCount. В противном случае элемент сначала нажимается дальше, а затем проверяется, отличается ли он от предыдущего с .filter(([oldValue, newValue]) ...).

этот означает, что нужно задержать излучение на один до window (Это первое .bufferCount(2, 1).map(arr => arr[0]).

или, может быть, легче контролировать порядок подписки себя publish():

const str = 'a-a-a-a-a-b-b-b-b-c-c-c-c-d-d-d-e';
const source = Observable.from(str.split('-'), Rx.Scheduler.async).share();

const connectable = source.publish();

connectable
    .window(source
        .bufferCount(2, 1) // keep the previous and current item
        .filter(([oldValue, newValue]) => oldValue !== newValue)
    )
    .concatMap(obs => obs.toArray())
    .subscribe(console.log);

connectable.connect();

выход такой же.


может быть, кто-то может придумать что-то попроще, но это работает (скрипка:https://fiddle.jshell.net/uk01njgc/)...

let counter = 0;

let items = Rx.Observable.interval(1000)
.map(value => Math.floor(value / 3))
.publish();

let distinct = items.distinctUntilChanged()
.publish();

distinct
.map(value => {
  return items
  .startWith(value)
  .takeUntil(distinct);
})
.subscribe(obs => {
  let obsIndex = counter++;
  console.log('New observable');
  obs.subscribe(
    value => {
      console.log(obsIndex.toString() + ': ' + value.toString());
    },
    err => console.log(err),
    () => console.log('Completed observable')
  );
});

distinct.connect();
items.connect();

вот вариант, который завершает все совместное использование suscription для вас...

const stream = ...;

// an Observable<Observable<T>>
// each inner observable completes when the value changes
const split = Observable
  .create(o => {
    const connected = stream.publish();

    // signals each time the values change (ignore the initial value)
    const newWindowSignal = connected.distinctUntilChanged().skip(1);

    // send the observables to our observer
    connected.window(newWindowSignal).subscribe(o);

    // now "start"
    return connected.connect();
  });