3

I'm having a very specific problem or misunderstanding with rxjava that someone hopefully can help with.

I'm running rxjava 2.1.5 and have the following code snippet:

public static void main(String[] args) {

    final Observable<Object> observable = Observable.create(emitter -> {
        // Code ... 
    });

    observable.subscribeOn(Schedulers.io())
            .retryWhen(error -> {
                System.out.println("retryWhen");
                return error.retry();
            }).subscribe(next -> System.out.println("subscribeNext"),
                         error -> System.out.println("subscribeError"));

}

After executing this, the program prints:

retryWhen

Process finished with exit code 0

My question, and what I don't understand is: Why is retryWhen called instantly upon subscribing to an Observable? The observable does nothing.

What I want is retryWhen to be called when onError is called on the emitter. Am I misunderstanding how rx works?

Thanks!

Adding new snippet:

public static void main(String[] args) throws InterruptedException {

    final Observable<Object> observable = Observable.create(emitter -> {
        emitter.onNext("next");
        emitter.onComplete();
    });

    final CountDownLatch latch = new CountDownLatch(1);
    observable.subscribeOn(Schedulers.io())
            .doOnError(error -> System.out.println("doOnError: " + error.getMessage()))
            .retryWhen(error -> {
                System.out.println("retryWhen: " + error.toString());
                return error.retry();
            }).subscribe(next -> System.out.println("subscribeNext"),
                         error -> System.out.println("subscribeError"),
                         () -> latch.countDown());

    latch.await();
}

Emitter onNext and complete is called. DoOnError is never called. Output is:

retryWhen: io.reactivex.subjects.SerializedSubject@35fb3008 subscribeNext

Process finished with exit code 0

Sason Ohanian
  • 795
  • 5
  • 16

2 Answers2

4

retryWhen calls the provided function when an Observer subscribes to it so you have a main sequence accompanied by a sequence that emits the Throwable the main sequence failed with. You should compose a logic onto the Observable you get in this Function so at the end, one Throwable will result in a value on the other end.

Observable.error(new IOException())
    .retryWhen(e -> {
         System.out.println("Setting up retryWhen");
         int[] count = { 0 };
         return e
            .takeWhile(v -> ++count[0] < 3)
            .doOnNext(v -> { System.out.println("Retrying"); });
    })
    .subscribe(System.out::println, Throwable::printStackTrace);

Since the e -> { } function body is executed for each individual subscriber, you can have a per subscriber state such as retry counter safely.

Using e -> e.retry() has no effect because the input error flow never gets its onError called.

akarnokd
  • 69,132
  • 14
  • 157
  • 192
0

One issue is, that you don't receive any more results because you'r creating a Thread using retryWhen() but your app seems to finish. To see that behaviour you may want to have a while loop to keep your app running.

That actually means that you need to add something like that to the end of your code:

while (true) {}

Another issue is that you dont emit any error in your sample. You need to emit at least one value to call onNext() else it wont repeat because it's waiting for it.

Here's a working example which a value, then it emits an error and repeat. you can use

.retryWhen(errors -> errors)

which is the same as

.retryWhen(errors -> errors.retry())

Working sample:

 public static void main(String[] args) {
        Observable
                .create(e -> {
                    e.onNext("test");
                    e.onError(new Throwable("test"));
                })
                .retryWhen(errors -> errors.retry())
                .subscribeOn(Schedulers.io())
                .subscribe(
                        next -> System.out.println("subscribeNext"),
                        error -> System.out.println("subscribeError"),
                        () -> System.out.println("onCompleted")
                );

        while (true) {

        }
    }

The reason why you need to emit a result is, that Observable needs to emit a value, else it wait until it receives a new one.

This is because onError can only be called onec (in subscribe), but onNext emits 1..* values.

You can check this behaviour by using doOnError() which provides you the error everytime it retrys the Observable.

Observable
            .create(e -> e.onError(new Exception("empty")))
            .doOnError(e -> System.out.println("error received " + e))
            .retryWhen(errors -> errors.retry())
            .subscribeOn(Schedulers.io())
            .subscribe(
                    nextOrSuccess -> System.out.println("nextOrSuccess " + nextOrSuccess),
                    error -> System.out.println("subscribeError")
            );
Emanuel
  • 8,027
  • 2
  • 37
  • 56
  • My snippet was just a reproduction of my problem. The real code calls the emitters onNext error and complete... The thing is that onError on the emitter is never called. retryWhen is called in call to subscribe, which i find weird and wrong. Given the code i posted, which is runnable. Should the function setup in retryWhen be called? Is that be correct behavior? – Sason Ohanian Oct 25 '17 at 08:11
  • Put a .doOnError() between and you'll see thats really going on behind the scenes :-) – Emanuel Oct 25 '17 at 08:13
  • I'm posting a new example. – Sason Ohanian Oct 25 '17 at 08:16
  • hold on, starting netbeans – Emanuel Oct 25 '17 at 08:25
  • I think my misunderstanding is that retryWhen takes a function with an observable as input and not an actual error. – Sason Ohanian Oct 25 '17 at 08:26
  • And you dont emit any error in your updated sample which means that doOnError and retryWhen is not called. Do you want to use repeatWhen which repeats when onComplete is called? – Emanuel Oct 25 '17 at 08:27
  • No, I want to the subscription to retry forever when it fails. – Sason Ohanian Oct 25 '17 at 08:30
  • RetryWhen is called. Try running the code. If you debug the code you will see that it is called in the subsribe method. As I said, the retry when function is called with an observable and not an exception as I thought. – Sason Ohanian Oct 25 '17 at 08:31