-
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
Decouple getting a subscription from subscribing #547
Comments
On branch 2phase, this test Func1<Integer, Integer> expensive = new Func1<Integer, Integer>() {
@Override
public Integer call(Integer t1) {
System.out.println("omg this is so expensive");
return t1;
}
};
Action1<Integer> printIt = new Action1<Integer>() {
@Override
public void call(Integer t1) {
System.out.println("Got " + t1);
}
};
@Test
public void go() {
Observable<Integer> o = Observable.from(1, 2, 3).map(expensive).take(1);
o.getSubscription().subscribe(printIt);
} outputs
On branch master (removing So I don't really get your point. I thought you wanted to have only this output:
Could you please elaborate? |
Why complicate things unnecessarily. You should never use a synchronous source in the matter you describe in the first place. That's why schedulers exist. A synchronous source is only acceptable for doing cheap short work. |
I hadn't rewritten the from, map or take operators to use the two phase subscriptions yet. I've just committed the a unit test you wrote and made it work. |
I don't think it unnecessary. If synchronous Observables shouldn't be used than why do they exist. Users can't tell if an Observable is synchronous or not. If code is written that based on an Observable that is asynchronous and it is later changed to synchronous then it could have unintended consequences. Adding a thread doesn't guarantee that the receiver will get scheduled to issue the unsubscribe in time. |
BTW, The subscribers to this chain of observables will see the correct result be the author might not realize that there is unnecessary computation going on because the expensive operation is also done for 2 and 3 and thrown away be the take operator. In C# new[]{ 1,2,3}.ToObservable().Select(x => { Console.WriteLine("Expensive {0}", x); return x; }) prints as expected Expensive 1 Only is you use the immediate scheduler you get, as expected since Immediate is not really a proper scheduler (and should be used with extreme care, if any) Expensive 1 But all all other schedulers behave properly. Too lazy right now to check which scheduler is used by default for this in RxJava, but we should use CurrentThread, which should take care of your original problem. |
@abersnaze as I point out, it should work if you use the current thread scheduler. Synchronous observables are like extremely sharp knives. Use them only when you know what you are doing, or you risk to cut your limbs off. |
CurrentThreadScheduler only seems to work at the granularity of subscribe or not to subscribe to an Observable. On the master branch I put these two unit tests. The @headinthebox Is there something that I'm missing that will make
|
List v = Observable.from(1, 2, 3).map(expensive).subscribeOn(sched) |
I've created a new branch in my fork called 1phase where I've been testing out your suggestions. I haven't been able to get the unit tests working with observerOn(currentThreadScheduler). I found these comments in the
|
You should no start with Observable.from(1, 2, 3) and avoid ImmediateScheduler like the plague. *_Never ever_* use the Observable.from(1,2,3) overload. Instead use the one below and pass CurrentThreadScheduler public static Observable from(Iterable<? extends T> iterable, Scheduler scheduler) { |
Sorry if I didn't make this clear but that is what I did in the 1phase branch but I still didn't see any change in behavior. |
This is related to fixing the |
The new Subscriber/Operator/OnSubscribe should handle this case. Can you check & close? |
…asure met… (ReactiveX#556) * Issue ReactiveX#547: Added a Sliding Time Window implementations to measure metrics. * Issue ReactiveX#547: Added a Fixed size Sliding Window implementation which aggregates the last N calls and replaced the existing RingBitSet.
… support … (ReactiveX#564) * Issue ReactiveX#547: Adapted CircuitBreakerConfigurationProperties to support the new sliding window types.
…lector an… (ReactiveX#574) * Issue ReactiveX#547: Added slow call rate to CircuitBreakerMetricsCollector and TaggedCircuitBreakerMetrics.
I've been holding on to this for a while now because it's a major departure and I wanted to make sure that it was really necessary. Given all of the issues we've seen recently with Schedulers and other operators I think it's time to discuss.
Right now the core method of
Subscription subscribe(Observer)
just doesn't work well for synchronous source Observables because downstream operations don't get aSubscription
until after theObservable
has been force fed all the data. For exampleObservable.from([1,2,3]).map({ /* expensive operation here */ }).take(1)
The subscribers to this chain of observables will see the correct result be the author might not realize that there is unnecessary computation going on because the expensive operation is also done for 2 and 3 and thrown away be the take operator.
The solution that I'm proposing is to split subscribing to an
Observable
into two phases. The first step is to get the subscription with a new method onObservable
called:public PartialSubscription<T> getSubscription() {...}
Then define
PartialSubscription
with all of the subscribe methods to start the sending of data to the observer:The existing
subscribe
&OnSubscribeFunc
can be deprecated and implemented using partial subscriptions means for backwards compatibility of existing operator implementations but to truly get all of the advantages of this all of the operators would have to be revised.I've submitted an accompanying pull request with implementation implementation details.
The text was updated successfully, but these errors were encountered: