-
Notifications
You must be signed in to change notification settings - Fork 3k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Asynchronous unsubscribe method #4222
Comments
I believe I've seen this discussion in somewhere, but my github search failed to find specific issues. 😭 |
@kwonoj I could remember saying something about it, too, but could not find it. Then I recalled that it might have been on Stack Overflow. And it is: https://stackoverflow.com/q/52368064/6680611 With the same OP, too. I think that adding support for However, I do think that this could be done with a dedicated operator - or, perhaps, by allowing the callback passed to I'll have a think about it. |
this part I mostly agree. Some way to allow teardown asynchronously would be useful in some special cases definitely though. Has there been discussion around this on tc-39 or whatwg proposals? eager introduction to these kind change might going to create some diverges I presume. |
Not that I'm aware of.
I don't think any changes to the fundamentals are needed. It should be possible to have an asynchronous teardown facilitated by a user-land operator. |
I should note that some other libraries for functional reactive programming, such as
How would it work? Would it solve the example I gave? You could have an async finalize queue associated with a subscription in addition to parent subscriptions/etc. A special function or a different property might allow retrieving a promise for the queue using the It would basically create an additional stage in the lifecycle of a subscription, the finalization stage. I don't know if it would be possible to conserve the queue when piping to operators that aren't aware of its existence.
Since the answer to the question appeared to be "no," I decided to make it a feature request. I also decided to give the full example instead of the watered down version I gave in the SO question. |
@GregRos There are two problems:
The first is solvable, but I'm not sure about the second. The order in which unsubscriptions occur depends upon whether a source completes or errors or whether the unsubscription is initiated explicitly from the destination. I'm increasingly of the opinion that if you have dependent resources, you should not be relying upon unsubscription ordering to manage them. I'm still thinking about this. The |
Also, in v6, the your If you have resources that have to be torn down in a specific order, you should not rely upon the unsubscription order to do so. |
@cartant Is there any reason for that? It's very counter-intuitive. Teardown is supposed to happen in the reverse order of creation. So when using
When you unsubscribe, you're supposed to:
Since async creation of a resource is possible, async disposal of a resource also makes sense. But isn't possible right now. |
This is not true. There are other circumstances in which teardown won't happen in reverse order of creation:
Any asynchronous creation of a resource is contained within an observable's implementation. As shown in the linked operator, it's also possible for an observable's implementation to ensure any asynchronous teardown is performed before emitting a The problem is your relying upon unsubscription order and, AFAICT, that order is not guaranteed by the contract. It's not even guaranteed that observables will cease emitting notifications upon unsubscription:
|
I guess if the order in which subscriptions are closed is undefined, then my core assumption really is wrong. With this in mind, we should really put async disposal aside for now and just talk a bit more about the order of closing subscriptions. The intuition when acquiring disposable resources, is that they will be disposed in the order opposite to how they were acquired. This is pretty much universal whenever there is a In the same way, the intuition is that when you close a subscription S to an observable O, where S maintains subscriptions to another set of observables, then all parent subscriptions of S should be closed opposite to the order in which they were acquired. This doesn't concern, for example, one of the parent subscriptions completing or erroring and thus being torn down (which you mentioned in your example). Then the natural assumption is that the error or completion is propagated forward.
The observable contract doesn't talk about how operators are implemented at all. It doesn't even talk about parent subscriptions. This is where rxjs can make its guarantees about the operators it defines. Also, the order of disposal isn't related to whether an observable will emit messages once unsubscribed. I can't see a technical reason for why the order would be undefined. It's always possible to make this guarantee (or some other guarantee if you like). Unlike the observable contract, which is designed to be as minimal as possible so that it would be easy to implement, |
Op> This is pretty much universal whenever there is a My understanding is that It would be possible to have the subscriptions to the inner observables unsubscribed before the source observable upon explicit unsubscription. All that would be needed would be for the inner subscriptions to be added to the destination subscription's array in such a was that they are added ahead of the source subscription. However, I'm not sure doing so is especially useful, as, IIRC, the source will be unsubscribed first if it completes or errors. And I'm deeply suspicious of any use case that precludes observables from emitting complete or error notifications because doing so would see subscriptions unsubscribed in an unwanted order. I'm also not convinced that guarantees regarding unsubscription ordering can be made, as operators can control when subscription and unsubscription occurs. RxJS is able to guarantee that observables won't emit after a complete or error notification, but I don't see how unsubscription order could be anything more than a guideline. |
I don't really see how I'm actually not just talking about the Anyway, while the source does not own inner observables, we're not talking about inner observables. We're specifically talking about subscriptions, which definitely are owned and encapsulated by child subscriptions (for our definition of child subscription), and (as is already the case), a child subscription is responsible for closing its parent subscriptions which it explicitly acquired.
Yup, this behavior is exactly what I want. All I'm talking about is that, in the event a deterministic disposal is called for, owned subscriptions will be disposed in a deterministic order.
I'm not talking about some kind of construct in the code that will make sure operators behave correctly and crash them if they don't. An operator is free to open subscriptions and never close them at all, at least technically. But a well-behaved operator probably shouldn't do that. What I'm hoping is for the documentation say that "the order of deterministic disposal is X," and for the operators provided in the library to work this way. That is, for the rxjs-specific operator contract to say this. If someone writes an operator that doesn't work that way (somewhere else), well, it just won't precisely fit the contract. (Actually, come to think of it, I don't even remember anything in the documentation saying that dependency/parent subscriptions will be closed, or talking about when |
I'll give your comment more thought tomorrow - I'm off to bed - but regarding the above, have a look at the current implementation of |
That's alright. Thanks for your time - and goodnight! 😄
Oh. That's a bummer. But I see people do think it's an issue, so I guess there is some consensus that it should happen? I think an equally big problem is there being no information about the behavior in the docs. |
Any news about it? |
I added an item to my to-do list to write up an explanation for how subscription and unsubscription works, but didn't get around to it. And here we are, a year later, and it's still on my list. |
according to http://reactivex.io/documentation/contract.html it would be valid to emit notifications to observers after unsubscribe. so when calling unsubscribe, the subscription could just block/ignore/not forward subsequent the agency over "ordered" unsubscription would be put into the observable's (generator) control, since it may delay its final complete/error until its source/all its sources have issued their complete/error. it could also just escalate the teardown notification and complete immediately, letting the sources dangling while they teardown (errors during their teardown would be uncatchable?) |
Hey guys! We need this functionality too, please add |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Feature Request
Brief
I want Subscriptions to support an async
unsubscribe
method, i.e. one that returns aPromise
, and I want some operators behave differently to accomodate this.The problem
I have a cold observable
base
that creates a new resource on subscription and disposes of it on unsubscribe.The thing is that
res.dispose()
is async because it sends a request to a server, which is required to properly dispose of the resource.Then, I use the
base
observable to create a dependent observable:In rxjs, if I close the subscription to
derived
, firstdres.dispose()
will be called and only then willbase.dispose()
will be called.Although I have not seen this in the documentation, it is the intuitive behavior and it does behave like this in practice.
However, in this case, the disposal is asynchronous. Rxjs doesn't support async disposal, so the two calls can effectively happen in parallel, and
base
might be disposed beforederived
, which is invalid behavior (derived
is still usingbase
at this time, after all)Describe the solution you'd like
I want
rxjs
to allow for async disposal in order to make sure this kind of disposal works properly. This means awaiting on someunsubscribe
calls before launching some new ones, depending on the operator.An operator such as
flatMap
can first dispose of the subscriptions to the inner observables simultaneously usingPromise.all
, and only then dispose of the source sequence.I can provide more detailed behavior changes if this suggestion gains any support/feedback.
The
unsubscribe
function will returnvoid | Promise<void>
, which will also allow the caller to determine when the subscription has been truly disposed.Describe alternatives you've considered
I can't think of any other good solutions to the problem. If you can, please share them.
Other situations where this is helpful
It will be possible to signal to the caller that an async failure happened while unsubscribing. Right now, it's hard to say what should be done with a promise rejection in the
unsubscribe
method.As a corollary of (1), operators can use this knowledge to improve their behavior. For example, if disposing of a resource is async and takes time, a
retry
or similar operator should wait until disposal of the previous resource has finished before trying to create another one. This may be desirable if, for example, two resources of the same arguments cannot coexist.The text was updated successfully, but these errors were encountered: