Имейте withLatestFrom ждать, пока все источники не произведут одно значение
я использую withLatestFrom
оператор в RxJS обычным способом:
var combined = source1.withLatestFrom(source2, source3);
...активно собирать самые последние выбросы от source2
и source3
и выделяют все три значения только тогда, когда source1
излучает.
но я не могу гарантировать, что source2
или source3
будут получены значения до source1
производит значение. Вместо этого мне нужно подождать, пока все три источника не произведут хотя бы одно значение, прежде чем позволить withLatestFrom
сделать его вещь.
контракт должен быть: if source1
излучает тогда combined
будет всегда в конечном итоге испускают, когда другие источники, наконец, производят. Если source1
испускает несколько раз, ожидая других источников, мы можем использовать последнее значение и отбросить предыдущие значения. Edit: как мраморная схема:
--1------------2---- (source)
----a-----b--------- (other1)
------x-----y------- (other2)
------1ax------2by--
--1------------2---- (source)
------a---b--------- (other1)
--x---------y------- (other2)
------1ax------2by--
------1--------2---- (source)
----a-----b--------- (other1)
--x---------y------- (other2)
------1ax------2by--
я могу сделать пользовательский оператор для этого, но я хочу убедиться, что мне не хватает очевидного способа сделать это с помощью ванили операторы. Это почти как я хочу combineLatest
для начального излучения, а затем до переключатель to withLatestFrom
С тех пор, но я не смог выяснить, как это сделать.
Edit: полный пример кода из final solution:
var Dispatcher = new Rx.Subject();
var source1 = Dispatcher.filter(x => x === 'foo');
var source2 = Dispatcher.filter(x => x === 'bar');
var source3 = Dispatcher.filter(x => x === 'baz');
var combined = source1.publish(function(s1) {
return source2.publish(function(s2) {
return source3.publish(function(s3) {
var cL = s1.combineLatest(s2, s3).take(1).do(() => console.log('cL'));
var wLF = s1.skip(1).withLatestFrom(s2, s3).do(() => console.log('wLF'));
return Rx.Observable.merge(cL, wLF);
});
});
});
var sub1 = combined.subscribe(x => console.log('x', x));
// These can arrive in any order
// and we can get multiple values from any one.
Dispatcher.onNext('foo');
Dispatcher.onNext('bar');
Dispatcher.onNext('foo');
Dispatcher.onNext('baz');
// combineLatest triggers once we have all values.
// cL
// x ["foo", "bar", "baz"]
// withLatestFrom takes over from there.
Dispatcher.onNext('foo');
Dispatcher.onNext('bar');
Dispatcher.onNext('foo');
// wLF
// x ["foo", "bar", "baz"]
// wLF
// x ["foo", "bar", "baz"]
5 ответов
Я думаю, что ответ более или менее так, как вы описали, пусть первое значение будет combinedLatest
, а затем перейти к withLatestFrom
. Мой JS туманен, но я думаю, что это будет выглядеть примерно так:
var selector = function(x,y,z) {};
var combined = Rx.Observable.concat(
source1.combineLatest(source2, source3, selector).take(1),
source1.withLatestFrom(source2, source3, selector)
);
вы, вероятно, должны использовать publish
чтобы избежать нескольких подписок, так что это будет выглядеть так:
var combined = source1.publish(function(s1)
{
return source2.publish(function(s2)
{
return source3.publish(function(s3)
{
return Rx.Observable.concat(
s1.combineLatest(s2, s3, selector).take(1),
s1.withLatestFrom(s2, s3, selector)
);
});
});
});
или с помощью функции стрелка...
var combined = source1.publish(s1 => source2.publish(s2 => source3.publish(s3 =>
Rx.Observable.concat(
s1.combineLatest(s2, s3, selector).take(1),
s1.withLatestFrom(s2, s3, selector)
)
)));
редактировать:
Я вижу проблему с concat
на withLatestFrom
не получение ценностей. Я думаю, что следующее будет работать:
var combined = source1.publish(s1 => source2.publish(s2 => source3.publish(s3 =>
Rx.Observable.merge(
s1.combineLatest(s2, s3, selector).take(1),
s1.skip(1).withLatestFrom(s2, s3, selector)
)
)));
...поэтому возьмите одно значение, используя combineLatest
, затем получите остальные, используя withLatestFrom
.
Я не был полностью удовлетворен принятым ответом, поэтому я нашел другое решение. Много способов освежевать кошку!
мой вариант использования включает только два потока-поток "запросов" и поток "токенов". Я хочу, чтобы запросы запускались, как только они будут получены, используя любой последний токен. Если маркера еще нет, он должен дождаться появления первого маркера, а затем запустить все ожидающие запросы.
Я не был вполне удовлетворен ответ был принят, и я нашел другое решение. По сути, я разделил поток запросов на две части - до и после прибытия первого токена. Я буферизую первую часть, а затем повторно освобождаю все за один раз, как только я знаю, что поток токенов не пуст.
const first = token$.first()
Rx.Observable.merge(
request$.buffer(first).mergeAll(),
request$.skipUntil(first)
)
.withLatestFrom(token$)
смотрите, он живет здесь:https://rxviz.com/v/VOK2GEoX
использовать combineLatest
и filter
чтобы удалить кортежи до первого полного набора, установите переменную для остановки фильтрации. Переменная может находиться в пределах области wrapping defer
чтобы делать вещи правильно (переподписки поддержки). Вот он в java (но те же операторы существуют в RxJs):
Observable.defer(
boolean emittedOne = false;
return Observable.combineLatest(s1, s2, s3, selector)
.filter(x -> {
if (emittedOne)
return true;
else {
if (hasAll(x)) {
emittedOne = true;
return true;
} else
return false;
}
});
)
у меня были аналогичные требования, но только для 2 наблюдаемых. Я закончил использование switchMap + first:
observable1
.switchMap(() => observable2.first(), (a, b) => [a, b])
.subscribe(([a, b]) => {...}));
Так:
- ждет, пока оба наблюдаемых выделяют некоторое значение
- вытаскивает значение из второго наблюдаемого, только если первый изменился (в отличие от combineLatest)
- не висит подписано на втором наблюдаемом (из-за.first ())
в моем случае вторым наблюдаемым является ReplaySubject. Я не уверен, что это будет работа с другими наблюдаемыми типами.
Я думаю, что:
- flatMap, вероятно, тоже будет работать
- возможно, этот подход можно расширить, чтобы обрабатывать более 2 наблюдаемых
Я был удивлен, что withLatestFrom
не будет ждать второго наблюдаемого.
на самом деле withLatestFrom
уже
- ждет каждого источника
- испускает только тогда, когда source1 испускает
- запоминает только последнее source1-сообщение, в то время как другие источники еще не запущены
// when source 1 emits the others have emitted already
var source1 = Rx.Observable.interval(500).take(7)
var source2 = Rx.Observable.interval(100, 300).take(10)
var source3 = Rx.Observable.interval(200).take(10)
var selector = (a,b,c) => [a,b,c]
source1
.withLatestFrom(source2, source3, selector)
.subscribe()
vs
// source1 emits first, withLatestFrom discards 1 value from source1
var source1 = Rx.Observable.interval(500).take(7)
var source2 = Rx.Observable.interval(1000, 300).take(10)
var source3 = Rx.Observable.interval(2000).take(10)
var selector = (a,b,c) => [a,b,c]
source1
.withLatestFrom(source2, source3, selector)
.subscribe()