我是RxJava的新手,所以我仍然试图理解它。我有Observable
代表按钮点击流,因此很热门。每次单击该按钮,我都想做一些I / O.如果失败了,我想重复并尝试再次进行I / O,直到成功为止。这似乎是使用retry()
或repeat()
的绝佳机会,但这些只能用于热观察,而不是冷。
这里有一些伪代码可以实现我想要做的事情:
buttonRequests
.map(actionEvent -> doIO())
.repeatAboveIfFailedUntilIOSucceeds()
.subscribe(...);
我考虑过使用flatMap
来复制该事件,而不是使用skip
忽略其余的事件,如果成功的话,但是我不能干净利落地得到一个不确定数量的事件。尝试。
考虑这个问题的正确方法是什么?
答案 0 :(得分:1)
请看一下测试。在每个事件中都会触发一个新的IO-Request。 Switch-Map就像Flat-Map,但是当新的上游事件进来时,它将取消订阅最近的订阅。如果您正在使用并发,Flat-Map将启动一个新的订阅。因此,假设您的hot observable触发了一个事件,flatMap开始在另一个线程(subscribeOn)上执行您的IO工作。如果另一个事件进入,而最后一个事件仍在执行,它将开始执行另一个IO任务。 Switch-Map将取消订阅最后一个并为当前事件启动一个。让我们看一下retry() - 运算符。重试只会重新订阅由ioWorkWrapped'提供的观察结果。直到observable完成onComplete。这可能非常危险,因为想象它会在每次尝试时失败。它会永远旋转。建议使用指数退避'指数退避'并且在X尝试之后提供备份可观察失败。对于'重试的用法当'请看一下这本优秀的书: Reactive Programming with RxJava
public class LibraryTest {
private AtomicInteger idx;
@Before
public void setUp() throws Exception {
idx = new AtomicInteger(0);
}
@Test
public void name() throws Exception {
Observable<String> stringObservable = Observable.just(1)
.switchMap(integer -> ioWorkWrapped()
.doOnError(throwable -> System.out.println("Something went wrong."))
.retry()
);
stringObservable.test()
.await()
.assertResult("value");
}
private Observable<String> ioWorkWrapped() {
return Observable.defer(() -> {
try {
Thread.sleep(500); // IO Work
if (idx.getAndIncrement() < 5) { // for testing...
return Observable.error(new IllegalStateException("Wurst"));
}
return Observable.just("value");
} catch (Exception ex) {
return Observable.error(ex);
}
});
}
}
答案 1 :(得分:0)
如果I / O操作失败,您需要使用运算符retryWhen
,您可以抛出在运算符中检查的Runnable异常。如果是这种类型的异常,你重试。
在此示例中,我们将重试4次。但是这种情况可以通过我们收到的throwable类型来改变。
int count=0;
@Test
public void retryWhenConnectionError() {
Subscription subscription = Observable.just(null)
.map(connection -> {
System.out.println("Trying to open connection");
connection.toString();
return connection;
})
.retryWhen(errors -> errors.doOnNext(o -> count++)
.flatMap(t -> count > 3 ? Observable.error(t) :
Observable.just(null).delay(100, TimeUnit.MILLISECONDS)),
Schedulers.newThread())
.subscribe(s -> System.out.println(s));
new TestSubscriber((Observer) subscription).awaitTerminalEvent(500, TimeUnit.MILLISECONDS);
}