You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
So there is now a pr for ref-counted, however I'm not convinced this is the best route to take.
For one it limits the ability of observables to even have cold-like behaviour such as replays (or custom implementations of ref-counting).
Secondly even with ref-counting some behaviours in the other motivating issue remain confusing for notably:
Making accidental repeated API requests (ex. calling subscribe twice and forgetting a replay)
continues to be confusing with the ref-counted behaviour as ref-counting only prevents double requests if subscriptions happen concurrently, but if a previous request has already finished then a new subscription will cause a new request regardless even if the author intended replaying the request.
And even more disastrously, if a request subscribes in the immediate between the .next() and .complete() the request will be ignored all-together, i.e. this:
constreq=newObservable(subscriber=>{fetch("foo.txt").then((response)=>{subscriber.next(response);subscriber.complete();});});reqObservable.subscribe({next: (res)=>{console.log("Got response outer");// in some nested call, synchronously:req.subscribe({next: (res)=>{console.log("Got response inner");},complete: ()=>{console.log("Complete inner");},});},complete: ()=>{console.log("Complete outer");},});
prints:
Got respone outer
Complete outer
Complete inner
in other words, the inner next is never actually called because it subscribed late, so misses the value, pretty useless for network requests or other promise adoption.
As a solution to both the limitations/confusions/disasters of both current approaches I suggest forcing the decision of whether observables are hot or cold, and whether they are eager or replay, to construction time. This means producers of observables actually have to consider what behaviour they want the observable to have rather than being surprised later.
To do this I propose making the actual constructor private and instead expose the different strategies as static methods:
typeSubscribeCallback<T>=(subscriber: Subscriber<T>)=>void;classObservable<T>{/* Behaves like the current proposal */staticlazyUnshared<T>(subscribeCallback: SubscribeCallback<T>): Observable<T>;/* Behaves like the ref-counted PR */staticlazyShared<T>(subscribeCallback: SubscribeCallback<T>): Observable<T>;/* Not currently proposed, but allows for re-using values, for example from requests */staticlazyReplay<T>(subscribeCallback: SubscribeCallback<T>): Observable<T>;/* Not currently proposed, but helps address the feedback from issue #170 of > API requests at unpredictable and undesirable times (too late when the UI is rendering or too early during critical loading) */staticeagerReplay<T>(subscribeCallback: SubscribeCallback<T>): Observable<T>;}
these could be used like:
// Each subscription gets it's own timerconstunsharedInterval=Observable.lazyUnshared(subscriber=>{constinterval=setInterval(()=>subscriber.next(),1000);subscriber.signal.onabort=()=>clearInterval(interval);});// Each subscription shares the same timer so will be alignedconstsharedInterval=Observable.lazyShared(subscriber=>{constinterval=setInterval(()=>subscriber.next(),1000);subscriber.signal.onabort=()=>clearInterval(interval);});// The call isn't started until subscription, however once it is called the same values will be replayed// on all future subscriptionsconstnetworkRequest=Observable.lazyReplay(subscriber=>{fetch("foo.txt").then(res=>{subscriber.next(res);subscriber.complete(res);});});// Same the previous example, except the callback is invoked immediately so subscription// might be able to receive the values earlier than neededconsteagerNetworkRequest=Observable.eagerReplay(subscriber=>{fetch("foo.txt").then(res=>{subscriber.next(res);subscriber.complete(res);});});
The text was updated successfully, but these errors were encountered:
This means producers of observables actually have to consider what behaviour they want the observable to have rather than being surprised later.
I'm assuming by "surprise" you mean the example you provided where you subscribe in between subscriber.next() and subscriber.complete(), and the new subscriber only receives complete and no values? I'm not sure that your proposal fixes this—if someone explicitly chooses the refcounted behavior, they can still subscribe in that fragile time and they have to handle that case.
We haven't heard that subscribing at that fragile time in a real footgun that we can expect to run into. And certainly haven't heard that it's more of a footgun than the always-cold version of this proposal which had side effects on every single subscribe() call.
I think additional configuration options could be added later if clear patterns develop that imply their need. But before we optimize prematurely and complicate the proposal, I think it makes sense to land the most predictable set of minimally-foot-gunny behavior we've agreed on in other discussions.
So there is now a pr for ref-counted, however I'm not convinced this is the best route to take.
For one it limits the ability of observables to even have cold-like behaviour such as replays (or custom implementations of ref-counting).
Secondly even with ref-counting some behaviours in the other motivating issue remain confusing for notably:
continues to be confusing with the ref-counted behaviour as ref-counting only prevents double requests if subscriptions happen concurrently, but if a previous request has already finished then a new subscription will cause a new request regardless even if the author intended replaying the request.
And even more disastrously, if a request subscribes in the immediate between the
.next()
and.complete()
the request will be ignored all-together, i.e. this:prints:
in other words, the inner
next
is never actually called because it subscribed late, so misses the value, pretty useless for network requests or other promise adoption.As a solution to both the limitations/confusions/disasters of both current approaches I suggest forcing the decision of whether observables are hot or cold, and whether they are eager or replay, to construction time. This means producers of observables actually have to consider what behaviour they want the observable to have rather than being surprised later.
To do this I propose making the actual constructor private and instead expose the different strategies as static methods:
these could be used like:
The text was updated successfully, but these errors were encountered: