[英]RxJava: combine two optional observables
I have two Observable
s, let's call them PeanutButter
and Jelly
. 我有两个
Observable
,我们称之为PeanutButter
和Jelly
。 I'd like to combine them to a Sandwich
Observable
. 我想将它们与
Sandwich
Observable
结合起来。 I can do that using: 我可以这样做:
Observable<PeanutButter> peanutButterObservable = ...;
Observable<Jelly> jellyObservable = ...;
Observable<Sandwich> sandwichObservable = Observable.combineLatest(
peanutButterObservable,
jellyObservable,
(pb, j) -> makeSandwich(pb, j))
The problem is that RX waits for the first PeanutButter
and the first Jelly
to be emitted before emitting the first combined Sandwich
but Jelly
may never be emitted which means I never get the first Sandwich
. 问题是RX在发出第一个组合
Sandwich
之前等待第一个PeanutButter
和第一个Jelly
,但Jelly
可能永远不会被释放,这意味着我永远不会得到第一个Sandwich
。
I'd like to combine the two feeds such that a combined item is emitted as soon as the first item from either feed is emitted, regardless of whether the other feed has yet to emit anything, how do I do that in RxJava? 我想组合这两个Feed,这样一旦从任一个Feed发出第一个项目就会发出组合项目,无论其他Feed是否还没有发出任何内容,我该如何在RxJava中执行此操作?
one possible approach would be to use the startWith
operator to trigger an emission of a known value from each stream upon subscription. 一种可能的方法是使用
startWith
运算符在订阅时触发每个流的已知值的发射。 this way combineLatest()
will trigger if either stream emits a value. 这样,如果任一流发出一个值,
combineLatest()
将触发。 you'd just have to be mindful of looking out for the initial/signal values in the onNext
consumer. 你只需要注意在
onNext
消费者中寻找初始/信号值。
something like this...: 像这样......:
@Test
public void sandwiches() {
final Observable<String> peanutButters = Observable.just("chunky", "smooth")
.startWith("--initial--");
final Observable<String> jellies = Observable.just("strawberry", "blackberry", "raspberry")
.startWith("--initial--");
Observable.combineLatest(peanutButters, jellies, (peanutButter, jelly) -> {
return new Pair<>(peanutButter, jelly);
})
.subscribe(
next -> {
final String peanutButter = next.getFirst();
final String jelly = next.getSecond();
if(peanutButter.equals("--initial--") && jelly.equals("--initial--")) {
// initial emissions
} else if(peanutButter.equals("--initial--")) {
// jelly emission
} else if(jelly.equals("--initial--")) {
// peanut butter emission
} else {
// peanut butter + jelly emissions
}
},
error -> {
System.err.println("## onError(" + error.getMessage() + ")");
},
() -> {
System.out.println("## onComplete()");
}
);
}
I think this problem can be approached by using merge
and scan
operators: 我认为可以通过使用
merge
和scan
运算符来解决此问题:
public class RxJavaUnitTestJava {
public Observable<Sandwich> getSandwich(Observable<Jelly> jelly, Observable<PeanutButter> peanutButter) {
return Observable.merge(jelly, peanutButter)
.scan(new Sandwich(null, null), (BiFunction<Object, Object, Object>) (prevResult, newItem) -> {
Sandwich prevSandwich = (Sandwich) prevResult;
if (newItem instanceof Jelly) {
System.out.println("emitted: " + ((Jelly) newItem).tag);
return new Sandwich((Jelly) newItem, prevSandwich.peanutButter);
} else {
System.out.println("emitted: " + ((PeanutButter) newItem).tag);
return new Sandwich(prevSandwich.jelly, (PeanutButter) newItem);
}
})
.skip(1) // skip emitting scan's default item
.cast(Sandwich.class);
}
@Test
public void testGetSandwich() {
PublishSubject<Jelly> jelly = PublishSubject.create();
PublishSubject<PeanutButter> peanutButter = PublishSubject.create();
getSandwich(jelly, peanutButter).subscribe(new Observer<Sandwich>() {
@Override
public void onSubscribe(Disposable d) {
System.out.println("onSubscribe");
}
@Override
public void onNext(Sandwich sandwich) {
System.out.println("onNext: Sandwich: " + sandwich.toString());
}
@Override
public void onError(Throwable e) {
System.out.println("onError: " + e.toString());
}
@Override
public void onComplete() {
System.out.println("onComplete");
}
});
jelly.onNext(new Jelly("jelly1"));
jelly.onNext(new Jelly("jelly2"));
peanutButter.onNext(new PeanutButter("peanutButter1"));
jelly.onNext(new Jelly("jelly3"));
peanutButter.onNext(new PeanutButter("peanutButter2"));
}
class Jelly {
String tag;
public Jelly(String tag) {
this.tag = tag;
}
}
class PeanutButter {
String tag;
public PeanutButter(String tag) {
this.tag = tag;
}
}
class Sandwich {
Jelly jelly;
PeanutButter peanutButter;
public Sandwich(Jelly jelly, PeanutButter peanutButter) {
this.jelly = jelly;
this.peanutButter = peanutButter;
}
@Override
public String toString() {
String jellyResult = (jelly != null) ? jelly.tag : "no jelly";
String peanutButterResult = (peanutButter != null) ? peanutButter.tag : "no peanutButter";
return jellyResult + " | " + peanutButterResult;
}
}
}
Output: 输出:
onSubscribe
emitted: jelly1
onNext: Sandwich: jelly1 | no peanutButter
emitted: jelly2
onNext: Sandwich: jelly2 | no peanutButter
emitted: peanutButter1
onNext: Sandwich: jelly2 | peanutButter1
emitted: jelly3
onNext: Sandwich: jelly3 | peanutButter1
emitted: peanutButter2
onNext: Sandwich: jelly3 | peanutButter2
The fact that Jelly
, PeanutButter
and Sandwich
are all independent types makes it a bit more complex around casting and nullability in scan
. Jelly
, PeanutButter
和Sandwich
都是独立类型的事实使得它在scan
铸造和可空性方面更加复杂。 If you have control over these types, this solution can be further improved. 如果您可以控制这些类型,则可以进一步改进此解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.