-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2.x: Improve JavaDoc of XObserver types. #5841
Conversation
Codecov Report
@@ Coverage Diff @@
## 2.x #5841 +/- ##
============================================
+ Coverage 96.38% 96.41% +0.02%
- Complexity 5812 5819 +7
============================================
Files 634 634
Lines 41760 41760
Branches 5796 5796
============================================
+ Hits 40251 40263 +12
+ Misses 591 579 -12
Partials 918 918
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great rewrite! But comment about errors from onX methods
* Calling {@link #onSubscribe(Disposable)}, {@link #onNext(Object)} or {@link #onError(Throwable)} with a | ||
* {@code null} argument is forbidden. | ||
* <p> | ||
* The implementations of the {@code onXXX} methods should avoid throwing runtime exceptions other than the following cases: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another valid case is fatal business logic violation. Otherwise people might think that swallowing errors is preferred because doc said so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by "fatal business logic violation"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When for some reason Observer
finds itself in inconsistent state during onXXX()
execution.
In this case exception thrown from onXXX()
will reach RxJavaPlugins
as OnErrorNotImplementedException
and in case of Android will crash the application, on JVM user can hook into that and specify required behavior.
This is fail fast design principle, when instead of swallowing error states and writing defensive code you fail as soon as possible preventing inconsistent states from going deeper into your system.
I'm just afraid that this doc might discourage people from that design principle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case exception thrown from onXXX() will reach RxJavaPlugins as OnErrorNotImplementedException and in case of Android will crash the application, on JVM user can hook into that and specify required behavior.
That is the property of the lambda-subscribe methods and not when using a plain Observer
.
The sentence is the derivative of the Reactive Streams rule 2.13 and is intended to discourage throwing from these methods as they may not end up where users expect it. Remember those issues where the sync code propagated the exception on the main thread but as soon as asynchrony was involved, no errors were suddenly thrown by the unit test. So don't throw from Observer
methods and you'll be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is the property of the lambda-subscribe methods and not when using a plain
Observer
.
Ah right, but exception is still delivered to RxJavaPlugins
' error handler.
The sentence is the derivative of the Reactive Streams rule 2.13 and is intended to discourage throwing from these methods as they may not end up where users expect it. Remember those issues where the sync code propagated the exception on the main thread but as soon as asynchrony was involved, no errors were suddenly thrown by the unit test.
Yep, RS 2.13, yep RxJava 2.x broke unit testing. However I still think throwing exception from onXXX
is okay if system finds itself in inconsistent state.
RS 2.13 says:
Calling
onSubscribe
,onNext
,onError
oronComplete
MUST return normally except when any provided parameter isnull
in which case it MUST throw ajava.lang.NullPointerException
to the caller, for all other situations the only legal way for aSubscriber
to signal failure is by cancelling itsSubscription
. In the case that this rule is violated, any associatedSubscription
to theSubscriber
MUST be considered as cancelled, and the caller MUST raise this error condition in a fashion that is adequate for the runtime environment.
Specifically:
In the case that this rule is violated, any associated Subscription to the Subscriber MUST be considered as cancelled, and the caller MUST raise this error condition in a fashion that is adequate for the runtime environment.
Which actually sort of allows throwing exceptions from onXXX()
since RxJava indeed provides a way to raise this error condition in a fashion that is adequate for the runtime environment
.
What I want to say is that there are thousands of projects where exceptions happen in onXXX()
methods, crash apps and then appear as fatal errors in bug tracking systems.
Correct solution will be to fix such errors by fixing the code that produces them in the first place rather than somehow not throwing them.
It's also unclear how one should avoid throwing exceptions from onXXX()
if Java has unchecked exceptions and some JVM languages like Kotlin treat every exception as unchecked. I hope you don't expect people to wrap code in every onXXX()
callback into a try/catch
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no guarantee where such exceptions will end up:
- if the flow is synchronous, the
subscribe
will end up throwing - if there is asynchrony, depending on the scheduler, the error may end up in
RxJavaPlugins.onError
,- The current thread's uncaught exception handler directly, or
- get swallowed in a custom scheduler implementation
From the Observable's perspective, an Observer is the end consumer thus it is the Observer
's responsibility to "crash further down". Yes, this means unreliable code should be wrapped into try-catch
, specifically in onError
or onComplete
. If there is a crash in onNext
, as the rule says, dispose the Disposable
and signal the error by some other means, for example, by calling this.onError()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this type of clarification to the JavaDoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation is getting slicker and slicker.
* <p> | ||
* When a {@code CompletableObserver} is subscribed to a {@link CompletableSource} through the {@link CompletableSource#subscribe(CompletableObserver)} method, | ||
* the {@code CompletableSource} calls {@link #onSubscribe(Disposable)} with a {@link Disposable} that allows | ||
* cancelling the sequence at any time. A well-behaved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we use cancelling
here or disposing
since the method is called dispose?
* When a {@code CompletableObserver} is subscribed to a {@link CompletableSource} through the {@link CompletableSource#subscribe(CompletableObserver)} method, | ||
* the {@code CompletableSource} calls {@link #onSubscribe(Disposable)} with a {@link Disposable} that allows | ||
* cancelling the sequence at any time. A well-behaved | ||
* {@code CompletableSource} will call a {@code CompletableObserver}'s {@link #onError(Throwable)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use may
call a since you can also just call onSubscribe and then do nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically yes, but we don't want to encourage that for end users because they'll just hang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright works for me.
* happens, it is the duty of the {@code CompletableObserver} implementation to be ready to receive multiple calls to | ||
* its methods and ensure proper concurrent behavior of its business logic. | ||
* <p> | ||
* Calling {@link #onSubscribe(Disposable)} or{@link #onError(Throwable)} with a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a space missing between or
and {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really agree with wording about try/catch, but also don't want to block this PR because it's 99% awesome
This PR improves the JavaDoc of the 4 RxJava main consumer types:
Observer
SingleObserver
MaybeObserver
CompletableObserver