我面临的问题如下: 我有两个observable,一个是从网络获取数据,另一个是从db获取数据。第二个可能是空的,但缺少第一个被认为是错误。然后,如果来自网络的结果,我需要将它与db的最新结果(如果存在)进行比较,如果它们不同,我想存储它们(如果db observable为空,我想要存储网络结果)。
是否有专门的操作员来处理这样的案例?
到目前为止,我尝试了一个带有zipWith的解决方案(如果db为空,它没有按预期工作),缓冲区(它正在工作,但远非理想), 我也尝试过flatmapping(需要在订阅者中额外投射)。
以下是缓冲区的解决方案。
Enter first name: Winston
Enter last name: Churchill
Enter phone number: +44 1 3425 9989 834
Enter age: 143
Exception in thread "main" java.lang.NullPointerException
at Group.add_user(groupData.java:53)
at Group.promtNewUser(groupData.java:45)
at groupData.main(groupData.java:8)
如果我修改latestRatesFromDb以便它不返回Observable而是和Optional相反,则整个问题变得微不足道,因为我可以使用此结果进行过滤。它接缝是没有办法以异步方式过滤(或者我错过了什么?)
答案 0 :(得分:1)
好的,这就是我写这篇文章的方法。
首先,任何具有differentThan
函数的类都应该更改为覆盖equals
。否则你不能对这些对象使用很多基本方法。
出于本示例的目的,我使用Integer
类作为我的类型参数编写了所有可观察对象。然后我使用调度程序编写两个模拟方法:
static Observable<Integer> ratesFromNetwork(Scheduler scheduler) {
return Observable.<Integer>create(sub -> {
sub.onNext(2);
sub.onCompleted();
}).delay(99, TimeUnit.MILLISECONDS, scheduler);
}
static Observable<Integer> latestRatesFromDB(Scheduler scheduler) {
return Observable.<Integer>create(sub -> {
sub.onNext(1);
sub.onCompleted();
}).delay(99, TimeUnit.MILLISECONDS, scheduler);
}
然而,正如您所看到的两者相似,它们会发出不同的值。
缺少第一个被认为是错误
实现此目标的最佳方法是使用timeout
。您可以在此处立即记录错误并继续:
final Observable<Integer> networkRate = ratesFromNetwork(scheduler)
.timeout(networkTimeOut, TimeUnit.MILLISECONDS, scheduler)
.doOnError(e -> System.err.println("Failed to get rates from network."));
当timeout
失败时,rx将抛出错误。 doOnError
将让您更好地了解此错误的起始位置,并让它传播到序列的其余部分。
第二个可能是空的
在这种情况下,我会做一个类似的策略,但是,不要让错误传播使用方法onErrorResumeNext
。现在,您可以使用firstOrDefault
确保observable至少发出一个值。在此方法中,使用一些您希望永远不会与网络结果匹配的虚拟值。
final Observable<Integer> databaseRate = latestRatesFromDB(scheduler)
.timeout(databaseTimeOut, TimeUnit.MILLISECONDS, scheduler)
.doOnError(e -> System.err.println("Failed to get rates from database"))
.onErrorResumeNext(Observable.empty())
.firstOrDefault(-1);
现在,通过使用distinct
方法,只有当它与之前的值不同时才能获取值(这就是为什么需要覆盖equals
)。
databaseRate.concatWith(networkRate).distinct().skip(1)
.subscribe(i -> System.out.println("Updating to " + i),
System.err::println,
() -> System.out.println("completed"));
此处将数据库速率放在网络速率之前,以利用distinct
。然后添加skip
以始终忽略数据库速率值。
完整代码:
final long networkTimeOut = 100;
final long databaseTimeOut = 100;
final TestScheduler scheduler = new TestScheduler();
final Observable<Integer> networkRate = ratesFromNetwork(scheduler)
.timeout(networkTimeOut, TimeUnit.MILLISECONDS, scheduler)
.doOnError(e -> System.err.println("Failed to get rates from network."));
final Observable<Integer> databaseRate = latestRatesFromDB(scheduler)
.timeout(databaseTimeOut, TimeUnit.MILLISECONDS, scheduler)
.doOnError(e -> System.err.println("Failed to get rates from database"))
.onErrorResumeNext(Observable.empty())
.firstOrDefault(-1);
databaseRate.concatWith(networkRate).distinct().skip(1)
.subscribe(i -> System.out.println("Updating to " + i),
System.err::println,
() -> System.out.println("completed"));
scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);
当networkTimeOut
和databaseTimeOut
大于100时,会打印:
Updating to 2
completed
当networkTimeOut
小于100时,它会打印:
Failed to get rates from network.
java.util.concurrent.TimeoutException
当databaseTimeOut
小于100时,它会打印:
Failed to get rates from database
Updating to 2
completed
如果您修改latestRatesFromDB
和ratesFromNetwork
以返回相同的值,则只会打印:
completed
如果您不关心强制超时或记录,那么归结为:
latestRatesFromDB().firstOrDefault(dummyValue)
.concatWith(ratesFromNetwork())
.distinct().skip(1)
.subscribe(this::save,
System.err::println,
() -> System.out.println("completed"));