private String stringResult=null;
private Throwable throwableResult=null;
@Test
public void whereIsTheThrowable() {
Observable.just("foo")
.map(this::justBlowUp)
.retryWhen(errors -> errors.zipWith(Observable.range(1, 3), (n, i) -> i))
.subscribe(s -> stringResult=s, throwable -> throwableResult=throwable);
assertNull(stringResult);
assertNotNull(throwableResult);
}
private String justBlowUp(String s) {
throw new RuntimeException();
}
That test fails with RxJava 2.1.7. retryWhen() appears to consume the Throwable, even after it no longer retries. The subscribe() lambda does not get any Throwable. While this test is silly (justBlowUp() just blows up), you can imagine an Observable chain where the work usually succeeds, only occasionally fails, and seldom fails four times in succession. However, in that case, it would be useful to have the Throwable for logging purposes.
retryUntil() does allow subscribe() to get the final Throwable... but then in retryUntil() we do not have the Throwable at all and cannot make decisions on it (e.g., retry N times if it seems to be an Internet connectivity error, but fail fast for everything else). retryWhen() seems to be the more powerful option, but how do we get the final Throwable, after retryWhen() stops retrying?
I could use a field to hold the Throwable, set inside the retryWhen() logic, but it feels like there should be a more idiomatic solution.
retryWhen treats the handler's completion as indicator for completing normally, therefore, the handler should fail after the retry options have been exhausted:
Observable.just("foo")
.map(this::justBlowUp)
.retryWhen(errors -> errors.flatMap(new Function<Throwable, Observable<Integer>>() {
int count;
@Override
public Observable<Integer> apply(Throwable error) {
if (count++ < 3) {
return Observable.just(count);
}
return Observable.error(error);
}
}))
.test()
.assertFailure(RuntimeException.class);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With