如何使用其他Observable过滤Observable

时间:2017-02-12 22:06:11

标签: java reactive-programming observable

我面临的问题如下: 我有两个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相反,则整个问题变得微不足道,因为我可以使用此结果进行过滤。它接缝是没有办法以异步方式过滤(或者我错过了什么?)

1 个答案:

答案 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);

networkTimeOutdatabaseTimeOut大于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

如果您修改latestRatesFromDBratesFromNetwork以返回相同的值,则只会打印:

completed

如果您不关心强制超时或记录,那么归结为:

latestRatesFromDB().firstOrDefault(dummyValue)
    .concatWith(ratesFromNetwork())
    .distinct().skip(1)
    .subscribe(this::save, 
        System.err::println, 
        () -> System.out.println("completed"));