From IObservable<T> to Task - task-parallel-library

So the case is this. Suppose somewhere I am filling a Collection. Each time an element is added, an IObservable calls OnNext for its subscribers.
Now, there will be a point where the collection will be filled. (I was reading something and I finished reading .. whatever). At that point, OnComplete() is called on the subscribers.
The user, however, won't observe this method. He will rather call an async method that he will await for ... he doesn't care much about the things he read, he just cares that he finished reading.
So basically, I want, from an IObservable, a Task that returns when the IObservable calls OnComplete() to its subscribers. I specifically want the user not to use an observer, but just to be happy with knowing that whatever happens after his await call will happen after the collection is filled.
Maybe the ToTask() method does the trick? I can't really tell by the documentation.

If you are using Rx 2.0 or later you can await IObservable which returns the last item in the observable. I.e. after the observable has completed.
var lastItem = await myObservable;
This is possible because in Rx 2.0 a GetAwaiter extension method on IObservable was added making it possible to await observables. There are also some handy extension methods that allow you to specify which element you want to await.
There is a nice blog about it here.

Related

Does toPromise() unsubscribe from the Observable?

I have not been able to find any normative text to answer this question. I have been using this code pattern (this is in TypeScript in an Angular application):
observeSomethingFun$: Observable<Fun>;
...
async loadsOfFun() {
const fun = await this.observeSomethingFun$.toPromise();
// I now have fun
}
In general, Observables need to be unsubscribed from. This happens automatically when the Angular async pipe is used but what happens in this case? Does toPromise unsubscribe after emitting one value?
If not, how do I unsubscribe manually?
Update:
It turns out #Will Taylor's answer below is correct but my question needs some clarification.
In my case the Observable emits a never-ending stream, unlike for example Angular's HttpClient Observables that complete after emitting one value. So
in my case I would never get past the await statement according to Taylor's answer.
RxJS makes this easy to fix. The correct statement turns out to be:
const fun = await this.observeSomethingFun$.pipe(first()).toPromise();
The RxJS first operator will receive the first value and unsubscribe from the source Observable. it will then send out that value to the toPromise operator
and then complete.
No need to unsubscribe.
Which is convenient, as there is no way to unsubscribe from a promise.
If the Observable completes - the promise will resolve with the last value emitted and all subscribers will automatically be unsubscribed at this point.
If the Observable errors - the promise will reject and all subscribers will automatically be unsubscribed at this point.
However, there are a couple of edge cases with toPromise in which the behavior is not completely clear from the docs.
If the Observable emits one or more values but does not complete or error, the promise will neither resolve or reject. In this case, the promise would hang around in memory, which is worth considering when working with toPromise.
If the Observable completes but does not emit a value, the promise will resolve with undefined.
First of all, thank you for this question and answer, I wrongly assumed toPromise() knew what it was doing in all scenarios and would unsubscribe when that observable completes (even if it is an observable stream)
So I will just say that it doesn't hurt to pipe all of your observables before using .toPromise()
I just went through a big ordeal of stepping through our app for memory leaks and found the above answer by Will to be good. The elaboration on the actual question was exactly the same issue I was running into.
We are stepping through each observable in the app right now and we use either
pipe(take(1)) which is equivalent to pipe(first()).
or we use pipe(takeUntil(this.destroyed)) where this.destroyed.next(true) is called when we destroy our particular component or service.
We use take() to keep our verbiage consistent so we can search for take or takeUntil across various components.
Long story short, yeah you might take a very slight performance hit piping your observables at each instance, but I highly recommend doing so in order to prevent any unwanted app-wide memory leak hunts. Then maybe if you have the time you can step through each one and see where .toPromise() actually unsubscribes correctly for you.

Expose a Subject to Callers But Be Notified when Subscriptions Drop to Zero

I have a service that I want to hand out a Subject (although it could be typed as an observable) as the result of a method call. This is straightforward, but what I really want is to be able to "detect" when its unsubscribe method is called (or since it could technically be handed out more than once to multiple subscribers, when its subscription count falls to zero). Is this possible?
If you take a look at the source code to a behavior subject
https://github.com/ReactiveX/rxjs/blob/master/src/internal/BehaviorSubject.ts
you will see how to extend a subject. You could do the same thing to create your own kind of subject that instead of taking a start value it takes a callback to be run on unsubscribe that passes in the observer count. You would need to return a custom subscription object as unsubscribe is done from the subscription.

Rxjs delay subscription to Subject to fire after complete

I have created this example to demonstrate the issue: https://stackblitz.com/edit/rxjs-vxvmq1?file=index.ts&devtoolsheight=100
Basically I want to call a function (e.g. cleanup as in the example) when a subject is completed. The function is called by others so not subscribing to the subject's changes, rather, a subscription is there to trigger the function for the completion of the subject. The function checks if it should do extra work when the subject is completed, so it checks if the subject has stopped or not. But it seems that once next() is called, the function is triggered before complete() is called, making the function thinks the subject has not been completed yet.
I wonder if there is any way to resolve this? Calling complete() first then next() didn't help as next() didn't notify the subscription after the subject has completed.
Long story short. It is all wrong.
Fix #1, not rxjs-idiomatic: you're subscribing wrongly: passing onNext callback, while you're interested in onComplete. There are three parameters taken by the subscribe: onNext, onError, onComplete and you are responsible for choosing the one you really need.
Fix #2, rxjs-idiomaric: you have to use pipe(...) along with operators defined in rxjs/operators (AFAIR). Note that it is easy to define your custom operator as well. So you either could use finalize(() => ...your cleanup logic goes here) or Observable.create returning finalizing logic as implementation of unsubscribe. Both nicely documented here or here.
In addition, seems that you're misunderstanding the semantics of rxjs. In terms of regex, it could be defined as next*(error|complete), which literally means: zero or infinitely many next's followed by either error or complete (exclusive or: never both simultaneously) exactly once. So don't expect next to do anything after complete (or equally error) fired.

Long running async method vs firing an event upon completion

I have to create a library that communicates with a device via a COM port.
In the one of the functions, I need to issue a command, then wait for several seconds as it performs a test (it varies from 10 to 1000 seconds) and return the result of the test:
One approach is to use async-await pattern:
public async Task<decimal> TaskMeasurementAsync(CancellationToken ctx = default)
{
PerformTheTest();
// Wait till the test is finished
await Task.Delay(_duration, ctx);
return ReadTheResult();
}
The other that comes to mind is to just fire an event upon completion.
The device performs a test and the duration is specified prior to performing it. So in either case I would either have to use Task.Delay() or Thread.Sleep() in order to wait for the completion of the task on the device.
I lean towards async-await as it easy to build in the cancellation and for the lack of a better term, it is self contained, i.e. I don't have to declare an event, create a EventArgs class etc.
Would appreciate any feedback on which approach is better if someone has come across a similar dilemma.
Thank you.
There are several tools available for how to structure your code.
Events are a push model (so is System.Reactive, a.k.a. "LINQ over events"). The idea is that you subscribe to the event, and then your handler is invoked zero or more times.
Tasks are a pull model. The idea is that you start some operation, and the Task will let you know when it completes. One drawback to tasks is that they only represent a single result.
The coming-soon async streams are also a pull model - one that works for multiple results.
In your case, you are starting an operation (the test), waiting for it to complete, and then reading the result. This sounds very much like a pull model would be appropriate here, so I recommend Task<T> over events/Rx.

How many "temperatures" are there for a Rx Observable?

All over the Rx.Net literature there are references to what is commonly know as the temperature of an observable.
There are cold observables (like the ones created by Observable.Interval() and similar factory methods), which will create side effects every time that a new Subscription is created.
On the other side of the spectrum there are hot observables (like Subject<T>) which will onboard new subscriptions as they come.
There are also warm observables, like the ones returned by RefCount() which will execute the initialisation every time one subscription is created, but only if there was no other active subscription. The behaviour of these warm observables is explained here by Dave Sexton:
Alternatively, you can call Publish then RefCount to get an IObservable that is shared among multiple consecutive observers. Note that this isn't truly a hot observable - it's more like a warm observable. RefCount makes a single subscription to the underlying observable while there's at least one observer of your query. When your query has no more observers, changing the reference count to 0, the underlying subscription is disposed. If another observer subscribes to your query later, moving the reference count from 0 to 1 again, then RefCount makes a new subscription to the underlying observable, causing subscription side-effects to occur again.
Are there any other temperatures that one should be aware of? Is it possible to obtain programmatically the temperature of an Observable?
Easy question first:
Is it possible to obtain programmatically the temperature of an Observable?
No. Best you can do is subscribe and see what happens.
The observable 'contract' specifies that when you subscribe to an observable you get zero or more OnNext messages, optionally followed by either one OnCompleted or one OnError message. The contract doesn't specify anything about how multiple or earlier/later subscribers are treated, which is what observable 'temperature' is mostly concerned with.
Are there any other temperatures that one should be aware of?
I wouldn't even think of it in such concrete or discrete terms as you have specified.
I think of it in terms of on-subscribe effects: The coldest of observables have all their effects happen on subscribe (like Observable.Return(42)). The hottest of observables have no effects happening on subscribe (new Subject<int>()). In between those two poles is a continuum.
Observable.Interval(TimeSpan.FromMilliseconds(100)) for example will emit a new number every 100 milliseconds. That example, unlike Observable.Return(42), could be mostly 'warmed-over' via .Publish().RefCount(): The first subscriber starts the numbers, but the second subscriber will see the only the latest numbers, not starting from 0. However, if instead of .Publish() you did .Replay(2).RefCount(), then you have some on-subscribe effects going on. Do the Publish and Replay observables have the same 'temperature'?
TL;DR: Don't focus on the classifications that much. Understand the difference between the two and know that some observables have colder properties and some have warmer ones.

Resources