Skip to content
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

refactor(SafeSubscriber): simplify methods #5676

Conversation

benlesh
Copy link
Member

@benlesh benlesh commented Aug 25, 2020

refactor(SafeSubscriber): simplify methods

  • Simplifies the call patterns in SafeSubscriber to not be quite so insane.
  • Note: for some reason one mocha test is passing, but still printing out the error it is catching in expect(fn).to.throw() in the console. Annoying, but I stepped through the RxJS code and verified the behavior was correct.

fix(bindNodeCallback): now emits errors that happen after callback

  • No longer logs to console warn
  • AsyncSubject overhead no longer required
  • Inlined the deprecated scheduled path

fix(bindCallback): now emits errors that happen after callback

  • No longer logs to console warn
  • AsyncSubject overhead no longer required
  • Inlined the scheduled path as is it deprecated

refactor(Observable): stop deprecated error handling from executing unnecessary code

  • Adds a few comments
  • Moves canReportError to the only place it is used

Related #5646

- Simplifies the call patterns in SafeSubscriber to not be quite so insane.
- Note: for some reason one mocha test is passing, but still printing out the error it is catching in `expect(fn).to.throw()` in the console. Annoying, but I stepped through the RxJS code and verified the behavior was correct.

Related ReactiveX#5646
@benlesh benlesh requested a review from cartant August 25, 2020 02:02
Copy link
Collaborator

@cartant cartant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes LGTM, but the problem with the error being logged - in the otherwise passing test - is due to this code:

      if (config.useDeprecatedSynchronousErrorHandling) {
        sink.syncErrorThrown = true;
        sink.syncErrorValue = err;
      }
      if (canReportError(sink)) {
        sink.error(err);
      } else {
        console.warn(err);
      }

I think this should be something like:

      if (canReportError(sink)) {
        sink.error(err);
      } else if (config.useDeprecatedSynchronousErrorHandling) {
        sink.syncErrorThrown = true;
        sink.syncErrorValue = err;
      } else {
        console.warn(err);
      }

I don't think errors should be reported via the error callback and thrown. And I don't think they should be written to the console and thrown either.

Also, I'm presuming that the binding of the subscriber's context to the standalone next callback - if useDeprecatedNextContext is enabled - is going to be done in another PR.

@cartant cartant mentioned this pull request Aug 25, 2020
13 tasks
@benlesh
Copy link
Member Author

benlesh commented Aug 25, 2020

Yeah. I didn't get to that. I hate that code, too. But I'm concerned as to why that's being hit, I'll revisit that in this PR i guess. I've been thinking about ways to make that cleaner.

- No longer logs to console warn
- AsyncSubject overhead no longer required
- Inlined the deprecated scheduled path
- No longer logs to console warn
- AsyncSubject overhead no longer required
- Inlined the scheduled path as is it deprecated
…nnecessary code

- Adds a few comments
- Moves canReportError to the only place it is used
@benlesh
Copy link
Member Author

benlesh commented Aug 25, 2020

Okay. A bit of yak-shaving, but overall I think it's worth it. I've updated my comment.

Basically, when looking for other usages of canReportError, I noticed that bindCallback and bindNodeCallback had incorrect behavior, where it should have emitted the error from a function that errors after the callback. If it's synchronous, it will next and then error, but the error should always be emitted.

That allowed me to move canReportError (which could be expensive, but I'll address that later) to Observable.ts.

In Observable.ts I noticed that we were hitting unnecessary code paths in the deprecated error handling case, and fixed that with a simple conditional.

I have plans in an upcoming PR to have a point to register a handler for these "unreportable errors" or whatever we want to call them. Once that is in place, we can do something where we only hit canReportError if there's a registered handler, and we'd never call console.warn again. (I hate that).

@benlesh benlesh requested a review from cartant August 25, 2020 17:12
Copy link
Collaborator

@cartant cartant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. New bindCallback implementations are really nice. I don't think the canReportError comment is accurate, though.

} else {
// This is hit when the user develops a poorly made observable. That means that
// something is calling `subscriber.error` more than once. We're currently logging
// this as a warning to console to let them know something is wrong.
Copy link
Collaborator

@cartant cartant Aug 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: This comment is not quite correct, AFAICT. Execution can only arrive here if an error is thrown - not emitted as a notification via the observer's error method. Specifically, the canReportError business was added because implementation errors - within the RxJS core - were being swallowed when unintentional errors where thrown after complete or error notifications had been made. And the swallowed errors meant the problems were hard-to-find.

IMO, we should edit the comment to reflect this. I feel strongly that - with the upcoming major version change - we should take the opportunity to simplify this and make it more general (in a separate PR). I am strongly of the opinion that any call to an observer's error method should effect a call to hostReportError - or some developer-configurable hook - rather than do what's done now if the subscriber is stopped: nothing (see here and here). I am of the opinion that there is no greater sin than swallowing errors - especially within a library - and if there is any hill upon which I would be prepared to die, this would be it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cartant. I have a follow-up PR here that adds a new configurable unhandled error hook, and removes this console.warn:

#5681

let hasResults = false;
let hasError = false;
let error: any;
return new Observable<T>((subscriber) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤ I like the new bindCallback implementations. Nice!

@benlesh benlesh merged commit c9ea2b0 into ReactiveX:master Aug 26, 2020
@benlesh
Copy link
Member Author

benlesh commented Aug 26, 2020

I've updated the comment and merged. (I was too impatient to wait for CI after all I did was update a comment, so if it breaks, it's my fault I guess. LOL)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants