19

I'm using RxJava in and Android application with RxAndroid. I'm using mergeDelayError to combine two retro fit network calls into one observable which will process emitted items if either emits one and the error if either has one. This is not working and it is only firing off the onError action when either encounters an error. Now to test this I shifted to a very simple example and still the successAction is never called when I have an onError call. See example below.

Observable.mergeDelayError(
                Observable.error(new RuntimeException()),
                Observable.just("Hello")
            )
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .finallyDo(completeAction)
            .subscribe(successAction, errorAction);

The success action will only be called if I use two success observables. Am I missing something with how mergeDelayError is supposed to work?

EDIT:

I've found that if I remove the observeOn and subscribeOn everything works as expected. I need to specify threads and thought that was the whole point of using Rx. Any idea why specifying those Schedulers would break the behavior?

Bobbake4
  • 24,509
  • 9
  • 59
  • 94
  • the doc says `an onError notification from any of the source Observables will (...) terminate the merged Observable.` seems clear from the diagram that any error is delayed until after all others are completed, and then is fired, terminating the observable without it being completed. – njzk2 Aug 21 '15 at 03:12
  • so you should indeed receive the `successAction` once, then the error action. (but not the complete) – njzk2 Aug 21 '15 at 03:15
  • how do you assert that success is not called? – njzk2 Aug 21 '15 at 03:16
  • I just have log statements in each action, nothing else. Seems like this is not behaving as the docs describe but I'm unsure why that is. – Bobbake4 Aug 21 '15 at 13:44
  • 1
    I tried it, and I observe the same behavior as you do. According to the source, it seems that any `onError` received by the merge operator will mark it as completed, which probably prevents it from transmitting any further onNext. I don't think this is how it should behave. – njzk2 Aug 21 '15 at 17:00

3 Answers3

18

Use .observeOn(AndroidSchedulers.mainThread(), true) instead of .observeOn(AndroidSchedulers.mainThread()

public final Observable<T> observeOn(Scheduler scheduler, boolean delayError) {
        return observeOn(scheduler, delayError, RxRingBuffer.SIZE);
    }

Above is the signature of observeOn function. Following code works.

  Observable.mergeDelayError(
                Observable.error(new RuntimeException()),
                Observable.just("Hello")
        )
                .observeOn(AndroidSchedulers.mainThread(), true)
                .subscribeOn(Schedulers.io())
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(String s) {

                    }
                });

Got this trick from ConcatDelayError thread: https://github.com/ReactiveX/RxJava/issues/3908#issuecomment-217999009

nizam.sp
  • 4,002
  • 5
  • 39
  • 63
  • 4
    I think this should be the accepted answer! I've spent my day debugging this issue. Anyway its very confusing that you have to specify 2 times that you wish to delay the error! – Tomas Bartalos Jan 09 '17 at 15:28
5

This still seems like a bug in the mergeDelayError operator but I was able to get it working by duplicating the observerOn and Subscribe on for each observable.

Observable.mergeDelayError(
            Observable.error(new RuntimeException())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribeOn(Schedulers.io()),
            Observable.just("Hello")
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribeOn(Schedulers.io())
        )
        .finallyDo(completeAction)
        .subscribe(successAction, errorAction);
Bobbake4
  • 24,509
  • 9
  • 59
  • 94
  • This is somehow correct, the mergeDelayError seems to do something with the responses that if you add a flatmap to any of the original requests, it will instead send them to the observeOn thread instead of their own defined thread – htafoya Aug 30 '22 at 19:01
1

I think you don't wait for the terminal event and the main thread quits before the events are delivered to your observer. The following test passes for me with RxJava 1.0.14:

@Test
public void errorDelayed() {
    TestSubscriber<Object> ts = TestSubscriber.create();
    Observable.mergeDelayError(
            Observable.error(new RuntimeException()),
            Observable.just("Hello")
        )
        .subscribeOn(Schedulers.io()).subscribe(ts);

    ts.awaitTerminalEvent();

    ts.assertError(RuntimeException.class);
    ts.assertValue("Hello");
}
akarnokd
  • 69,132
  • 14
  • 157
  • 192