Skip to content

subscribe vs unsafeSubscribe #3344

@vadims

Description

@vadims
Contributor

What's the difference between subscribe and unsafeSubscribe?

The javadoc mentions that unsafeSubscribe should only be used for Observable.Operator, I'm assuming that includes Observable.OnSubscribe. Also, it briefly mentions nested subscriptions but it doesn't explain the difference in behavior of subscribe and unsafeSubscribe.

Also, is there guidance on when Subscribers.wrap should be used?

Here's a snippet that I thought would behave the same, however unsafeSubscribe is working and subscribe is timing out.

  public static Observable<Long> foo() {
    return Observable.create(subscriber -> {
      Observable.timer(1, TimeUnit.MILLISECONDS)
          .unsafeSubscribe(Subscribers.wrap(subscriber));
    });
  }

  public static Observable<Long> foo2() {
    return Observable.create(subscriber -> {
      Observable.timer(1, TimeUnit.MILLISECONDS)
          .subscribe(Subscribers.wrap(subscriber));
    });
  }

  public static void main(String[] args) {
    long result = foo().flatMap(value -> foo())
        .timeout(1, TimeUnit.SECONDS)
        .toBlocking().single();
    System.out.println("result = " + result);


    long result2 = foo2().flatMap(value -> foo2())
        .timeout(1, TimeUnit.SECONDS)
        .toBlocking().single();
    System.out.println("result2 = " + result2);
  }
result = 0
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.TimeoutException
    at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:455)
    at rx.observables.BlockingObservable.single(BlockingObservable.java:331)
    at Foo.main(Foo.java:39)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.util.concurrent.TimeoutException
    at rx.internal.operators.OperatorTimeoutBase$TimeoutSubscriber.onTimeout(OperatorTimeoutBase.java:169)
    at rx.internal.operators.OperatorTimeout$1$1.call(OperatorTimeout.java:42)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Activity

akarnokd

akarnokd commented on Sep 14, 2015

@akarnokd
Member

Hello,

the difference is that subscribe wraps the Subscriber into a SafeSubscriber which captures exceptions thrown from the wrapped Subscriber's onXXX methods and unsubscribes both the upstream and downstream on a terminal event. unsafeSubscribe doesn't do this and is mainly there to avoid the wrapping overhead as much as possible.

What happens here with foo2 is that this downstream-directed unsubscription from SafeSubscriber cancels the merge operation which then cancels the inner foo2 call and simply stops emitting anything (not even terminal events) and the next operator - timeout - will time out.

Generally, I'd warn against using create to roll your own source because there are subtle and less-subtle things that can go wrong.

vadims

vadims commented on Sep 14, 2015

@vadims
ContributorAuthor

Thanks for the explanation, I think the javadoc could be improved to mention this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @vadims@akarnokd

        Issue actions

          subscribe vs unsafeSubscribe · Issue #3344 · ReactiveX/RxJava