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();
});