-
Notifications
You must be signed in to change notification settings - Fork 3k
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
feat(takeUntil): change behavior of takeUntil to not subscribe to source if notifier is synchronous #2189
Conversation
So looking at this, it changes the behavior of While that's definitely a performance tweak, there's a tradeoff being made here. A previous version of RxJS would have subscribed to the source observable. Subscription is a common point at which side effects occur. For example, in the scenario that the source was an ajax POST. The data would have been posted. Maybe the teardown wants to "unpost" it by rolling back, maybe it doesn't. The point is if there's a It's definitely not a "fix" per se... it's more of a feature change. @mattpodwysocki @staltz I'd be interested in your thoughts here. |
Hm, I may have messed up this PR. I was going off memory of the Rx4 behavior, which we currently don't have either. Though it seems like this may be what RxJava does? |
I'm generally favorable to this change, I think it makes more sense. But I don't have a hard opinion. It would be a breaking change, and this needs to be clearly communicated. For the record, most.js subscribes to the notifier before it subscribes to the source, and so does xstream. I think it's mostly a question of how to manage the breaking change and communication. Looking at the use cases, most of the cases where both source and notifier are synchronous should intuitively yield an empty Observable. And we don't have something like |
I'm totally in favor of the notifier being subscribed first. But I hesitate to not subscribe to the source if the notifier synchronously emits. I don't think most people would expect that behavior. The act of subscription is a common place for side effects and this would completely change the behavior with takeUntil to not trigger side effects that might be important to some systems. |
Can you come up with a compelling example? I tried, and couldn't come up with something non-silly. |
@staltz @Blesh initially I was concerned, but the more I think about it, it seems more correct that the source Observable is never subscribed. For example, what if the source makes an AJAX request, and the You might ask why would someone ever pass |
@staltz I did in my original comment. It's as simple as a
In the above, if both |
@Blesh yes, |
@trxcllnt since you can't know if a given observable is sync or async, |
I'm not against the change... but I would find it a little disturbing if no one thought of the implications of this breaking change. |
@Blesh To illustrate, I'll use everyone's favorite functional programming example, the fibonacci sequence! Here's a simple fibonacci sequence Observable: function fibs(period = 0) {
return Observable
.interval(period)
.scan([i, n] => [n, i + n], [1, 0])
.pluck(0)
} If you have a start button and user input field for the number of values to compute ahead of time, you can print 0-Infinity values from the fibonacci sequence over some period of time: starts.switchMap(() =>
fibs().take(textInput.value)
).subscribe(::console.log) But what if you have a start button and some reactive state, let's say a BehaviorSubject of checkbox values, that you can't know until subscribe? Something like: starts.switchMap(() =>
fibs().takeUntil(stops.filter(Boolean))
).subscribe(::console.log) In the first example
p.s. it also seems the reason this wasn't an issue in Rx < 5 was the default trampoline scheduling. If the notifier had been subscribed to first, the trampoline scheduler would have ensured that it didn't emit until after the stack had unwound, ensuring the |
and I'm shocked @mattpodwysocki is here arguing for more side-effects! ;-> but more curious if you have counter examples where it's imperative that |
@Blesh and thinking through your example of If the browser were smart enough to optimize against sending AJAX requests that are aborted in the same tick of the event loop, then we'd be fine. If not (and I think this is the case with HTTP |
Actually, I think As far as a counter point... what happens to people who expect: I can tell you're passionate about this change. I just don't think it's right that we change it. Probably shouldn't make a breaking change to support what seems to be an edge case that will break someone else's edge case. |
It could, but it'd defeat the purpose of shortcut fusion. Consistency is great, and we're both arguing for more of it. In the absence of a supplied scheduler, we've explicitly chosen to employ recursive scheduling semantics. This has always been acknowledged as a backwards-incompatible breaking change, but necessary for performance, and still correct as long as all the operators implement it consistently. We agree that right now, depending on how you look at it, either take or takeUntil needs to change to be consistent. I believe takeUntil needs to change in order to be correct with respect to immediate dispatch, thus consistent with the rest of the library. Think of it in terms of subscription order: given
It's possible to do this with
If this was more important than performance, we wouldn't have switched to recursive scheduling by default. Slight deviations in operator internals (such as subscription order) from past versions of Rx have already been made, and are to be expected. |
…mits a value, the source should 1360
0e669b8
to
a1b18ec
Compare
@trxcllnt haha... You've almost sold me. I think that I'd be okay with including this in the next version of RxJS, but I'm not okay implementing this as a "fix", because it's really a breaking change to a feature of the library. If anything, altering |
@Blesh, no objection to moving it to the next major. |
@Blesh I updated this PR to target the |
We need to get these changes to target what's in master now. I almost completely forgot about this PR. LOL |
@benlesh is this not making it into v6? |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Description:
If the notifier supplied to takeUntil synchronously emits a value, the source should not be subscribed to.
Related issue (if exists):
#1360
cc: @mattpodwysocki
Side note:
subscribeToResult
should definitely not returnnull
cc: @Blesh @jayphelps