-
Notifications
You must be signed in to change notification settings - Fork 26.2k
fix(core): fix proper propagation of subscriptions in EventEmitter #22016
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
fix(core): fix proper propagation of subscriptions in EventEmitter #22016
Conversation
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.
This looks good to me. @mhevery can you take a look as well?
}); | ||
|
||
it('unsubscribing a subscriber invokes the dispose method', () => { | ||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { |
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.
Does this test need to be async?
Also seems like neither this test nor the next fail before the fix. Should one of them be failing?
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.
This one is async because it needs to be sure the dispose method will be called. I know this one would probably work without async because Subjects in RxJS work synchronously but this would rely only on the Subject internals that might change.
The other tests are to make sure I didn't break anything related to this issue while fixing this.
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.
But you mentioned in your comment that there is a case where the teardown is not called? I don't see any failing test to demonstrate that case and I couldn't reproduce it myself...
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.
@jbedard That's the same use-case as the original example :).
emitter.pipe(filter(() => true)).subscribe().unsubscribe();
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.
This one?
// This simulates `FilterSubscriber` or `MapSubscriber`
const subscriber = new Subscriber(val => {});
subscriber.add(() => console.log('Subscriber disposed')); // This is never called - wrong
const ee2 = new EventEmitter();
ee2.subscribe(subscriber).unsubscribe();
console.log('EventEmitter - Subscriber', ee2.observers.length); // 0
// This did unsubscribe because we're holding the direct `Subscription` for `EventEmitter`
// but didn't trigger the teardown function. The teardown function is
// where it unsubscribes from its parent in `FilterSubscriber` or `MapSubscriber`.
Is that one in this PR?
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.
@jbedard Yes, that's the same problem as with this:
emitter.pipe(filter(() => true)).subscribe().unsubscribe();
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: #21999
Live demo: https://stackblitz.com/edit/angular-9kvrfk?file=src/app/app.component.ts
Right now the
EventEmitter
class doesn't properly remove observers when not subscribing directly toEventEmitter
and using an intermediateSubscription
object. In other words when chainingEventEmitter
with any operator that internally creates aSubscription
object theEventEmitter
isn't able to unsubscribe it properly by calling its teardown function.This can be seen in the two following examples:
For example if I take the first example with just
ee.pipe(map(() => true)).subscribe().unsubscribe();
it should work just like it does with the originalSubject
from RxJS 5.5:I described it in more detail in #21999 (comment) but basically the problem is that the
EventEmitter
creates functionsschedulerFn
,errorFn
andcompleteFn
that are insidesuper.subscribe()
wrapped with anotherSubscription
object which is then returned.However it never "connects" the original
generatorOrNext
(which isSubscription
object itself) with the newSubscription
returned fromsuper.subscribe()
.What is the new behavior?
Properly calls teardown functions from the original
Subscription
. The following tests would fail without this PR:Does this PR introduce a breaking change?
Other information
Closes #21999