Purpose of "completing" an rxjs Subject? - javascript

What is the purpose of calling complete() on rxjs Subject?
As an example:
Calling complete on takeUntil() notifier Observable. Why do we need to do that, and not just call next() and be done with it?
P.S. If it's just a convention, why is it so?

complete is normally called on subjects in order to send the completed event through the stream. This is done in order to trigger observers that wait for that notification. For example:
var subject = new BehaviorSubject<int>(2);
var subjectStream$ = subject.asObservable();
var finalize$ = subjectStream$.pipe(finalize(()=> console.log("Stream completed")));
var fork$ = forkJoin(subjectStream$,of(1));
....
finalize$.subscribe(value => console.log({value}));
//output: 2, notice that "Stream completed" is not logged.
fork$.subcribe(values => console.log({values});
// no output, as one of the inner forked streams never completes
Furthermore, is a security measure in order avoid mem. leaks, as calling complete on the source stream will remove the references to all the subscribed observers, allowing the garbage collector to eventually dispose any non unsubscribed Subscription instance.

Related

Is an Observer a 'listener' to an Observable in RxJS?

I'm learning RxJS and am rather confused as to where the "listeners" are (in the Observable or the Observer), how they are subscribed/unsubscribed, and what happens when an Observer is "no longer interested in" an Observable, such as when you use take or takeUntil.
For the first part -- what's subscribed to what, what's a listener -- I'm confused by the seeming contradiction between these statements. From http://reactivex.io/rxjs/manual/overview.html we read that Observers are not 'listeners' to Observables
This is drastically different to event handler APIs like
addEventListener / removeEventListener. With observable.subscribe, the
given Observer is not registered as a listener in the Observable. The
Observable does not even maintain a list of attached Observers.
but in http://reactivex.io/learnrx/ it says (Exercise 30) (highlighting mine) that
An Observable based on an Event will never complete on its own. The
take() function creates a new sequence that completes after a discrete
number of items arrive. This is important, because unlike an Event,
when an Observable sequence completes it unsubscribes all of its
listeners. That means that if we use take() to complete our Event
sequence, we don't need to unsubscribe!
This seems contradictory to me. When you set up an Observable with, for example, fromEvent, where is the event listener? When you use take(1), for instance, on an Observable based on DOM events, what happens after the first event is sent to the observer? Does the Observer unsubscribe from the Observable, which continues to emit events, it's just that the Observer isn't listening to them anymore? Or does the Observable somehow unsubscribe the Observer, that is, the eventListener was in the Observable, not the Observer?
Thanks for any clues -- obviously I'm not seeing the forest for the trees, but the tutorials I'm working through, while they are good at trying to explain it conceptually, leave me confused as to what's actually going on.
The first part is being rather particular about its use of words in order to highlight that subscribing to an observable is a matter of calling a function (or more likely a chain of functions) to run all the code they contain. The second part is less particular about its wording, but it's not really talking about the same thing. If you like, the second part would be better worded as "when an observable completes, it calls teardown logic on its observers.
Let me try to describe what i mean when i say that subscribing to an observable is a matter of calling a chain of functions. Consider the following super simple example:
For a super simple example, suppose i create this observable:
const justOne = Rx.Observable.create(function realSubscribe(observer) {
observer.next(1);
observer.complete();
});
justOne.subscribe(val => console.log(val));
If i then call justOne.subscribe(val => console.log(val)), doing so will immediately call the function i named realSubscribe. It then does observer.next(1), which results in logging out val, then it does observer.complete(). And that's it.
No where in this process did the observable create or augment a list of subscribers; it just ran through the code sequentially and then was done.
Now moving onto a slightly more realistic example, let's consider fromEvent. If i were to implement it, it might look something like this (the real implementation is more complicated, but this gets the gist of it):
function fromEvent(element, eventName) {
return Rx.Observable.create(function subscribeToEvent(observer) {
element.addEventListener(eventName, observer.next);
return function cleanup() {
element.removeEventListener(eventName, observer.next);
}
});
}
const observable = fromEvent(document, 'click');
const subscription = observable.subscribe(event => console.log(event));
Now when i call observable.subscribe, it runs subscribeToEvent, and in so doing it calls addEventListener on the document. document.addEventListener does result in the document keeping a list of event listeners, but that's because of the way addEventListener is implemented, not something common to all observables. The observable itself doesn't keep track of any listeners. It just calls what it's told to call, and then returns a cleanup function.
Next up let's look at take. As before the real implementation is more complicated, but here's roughly what it does:
// In the real `take`, you don't need to pass in another observable since that's
// available automatically from the context you called it in. But my sample code
// has to get it somehow.
function take(count, otherObservable) {
return new Observable(function subscribeToTake(observer) {
let soFar = 0;
otherObservable.subscribe((value) => {
observer.next(value);
soFar++;
if (soFar >= count) {
observer.complete();
}
});
});
}
const clickObservable = fromEvent(document, 'click');
take(1, clickObservable).subscribe(event => console.log(event))
As mentioned in the comment, the syntax i'm using doesn't quite match how it would be use in rxjs, but that's because to mimic that would require a more full implementation. Anyway, the main thing to draw your attention to is that we're starting to produce a chain of functions:
When i call .subscribe, that calls subscribeToTake. This sets up a counter, and then calls otherObservable.subscribe, which is subscribeToEvent. subscribeToEvent then calls document.addEventListener.
Take's job is to sit in the middle of this function chain. It keeps track of how many values have been emitted so far. If the count is low enough, it just forwards the values along. But once the count is reached, it will call complete, thus ending the observable. Calling complete causes the observable to run any teardown logic it has, or anything its chain has. There's no teardown logic for take, but fromEvent will run some teardown logic to remove the event listener.

RxJS Subscriber unsubscribe vs. complete

I was reading through the RxJS docs and want to make sure I'm understanding the difference between Subscriber.unsubscribe() and Subscriber.complete().
Let's say I have an observable with two subscribers, subscriber1 and subscriber2. If subscriber1 calls unsubscribe on their subscription, it will no longer receive notifications from the observable but subscriber2 will continue to receive them.
The docs for .complete():
The Observer callback to receive a valueless notification of type complete from the Observable. Notifies the Observer that the Observable has finished sending push-based notifications.
Does this mean that in the same scenario above, subscriber1 could call complete and it would end the observable and stop the stream for both subscriber1 and subscriber2?
Subscribers don't call complete(). You can call complete() on a Subject or more typically it's called for you "indirectly" using operators such as take(), takeWhile(), ...
For example:
const s = new Subject();
const subscriber1 = s.subscribe(...);
const subscriber2 = s.subscribe(...);
s.complete(); // both subscribers will receive the complete notification
// or with `take(1)` operator it'll call `complete()` for you
const subscriber1 = s.take(1).subscribe(...);
const subscriber2 = s.take(1).subscribe(...);
s.next(42); // both subscribers will receive the complete notification
Note that calling complete() on a Subject changes its inner state and there's no way to make it non-complete again while just unsubscribing a subscriber has no effect on the Subject.
A little similar question: Observable Finally on Subscribe
From my experience with the API, the idea is that: you don't call the Observable, the Observable calls you. You are able to trigger things if you create a Subject and next/complete the Subject though.
That's why you will see some examples that have a "private" Subject as a class member, but the publicly exposed item is an Observable. The expectation is that you will subscribe to the Observable and the top level class will dispatch values through the Subject with next() and error(). The only way to complete the Observable/Subject is to complete() the Subject.
Additionally, Subscriber does not have an unsubscribe() method, a Subscription does.

RxJS: BehaviorSubject unsubscribe

I am very new to observables am worried about memory leaks. If I create the following:
private client = new BehaviorSubject("");
clientStream$ = this.client.asObservable();
and susbscirbe to them in views like so:
this.clientService.clientStream$.subscribe(
client => {
this.client = client;
}
}
do I need to unsubscribe? What if I called client.getValue()?
do I need to unsubscribe?
Probably.
If you're designing a subject which will complete -- ie, if you intend to callclient.complete() (or client.onCompleted() if you're using rxjs 4) -- then this will tear down the subscriptions automatically.
But often times, your behavior subject will be in some service which persists, and you don't want it to complete. In that case, you will need to unsubscribe. There are two ways you can unsubscribe:
1) Manually:
When you call .subscribe, you get back a subscription object. If you call .unsubscribe() on it (.dispose() in rxjs 4), you will unsubscribe. For example:
const subscription = this.clientService.clientStream$
.subscribe(client => this.client = client);
setTimeout(() => subscription.unsubscribe(), 10000); // unsubscribe after 10 seconds
2) Automatically, based on another observable. If you're using observables often in your application, you will probably find this approach to be very convenient.
Observables have a .takeUntil operator, which you can pass in another observable to. When that second observable emits a value, it will do the unsubscription for you. This lets you describe up front what conditions should tear down your observable. For example:
this.clientService.clientStream$
.takeUntil(Observable.timer(10000))
.subscribe(client => this.client = client);
What if I called client.getValue()
That will synchronously give you the current value. You're not subscribing at all. On the up side, this means you won't need to unsubscribe. But on the downside, why are you using a behavior subject if you're not interested in seeing when the value changes?

in rx.js make source.subscribe await it's observer using async/await

I have an observer that goes like this.
var source = rx.Observable.fromEvent(eventAppeared.emitter, 'event')
.filter(mAndF.isValidStreamType)
.map(mAndF.transformEvent)
.share();
I then share it with a number of subscribers. These subscribers all take the event and perform some async operations on them.
so my subscribers are like
source.subscribe(async function(x) {
const func = handler[x.eventName];
if (func) {
await eventWorkflow(x, handler.handlerName, func.bind(handler));
}
});
There's a bit of extra stuff in there but I think the intent is clear.
I need every "handler" that handles this particular event to handle it and block till it gets back. Then process the next event.
What I've found with the above code is that it's just calling the event with out awaiting it and my handlers are stepping on themselves.
I've read a fair number of posts, but I can't really see how to do it. Most people are talking about making the observer awaitable. But that's not what I need is it? It seems like what I need is to make the observer awaitable. I can't find anything on that which usually means it's either super easy or a super ridiculous thing to do. I'm hoping for the former.
Please let me know if you need any further clarification.
---update---
what I have realized is that what I need is a fifo queue or buffer ( first in first out ), sometimes referred to as back pressure. I need all messages processed in order and only when the preceding message is done processing.
---end update---
at first I thought it was cuz I was using rx 2.5.3 but I just upgraded to 4.1.0 and it's still not synchronous.
There's no way to tell a source observable to put events on hold from within a subscribe, it just lets us "observe" incoming events. Asynchronous things should be managed via Rx operators.
For example, to let your asynchronous handlers process events sequentially, you could try to use concatMap operator:
source
.concatMap(x => {
const func = handler[x.eventName];
return func ?
eventWorkflow(x, handler.handlerName, func.bind(handler)) :
Rx.Observable.empty();
})
.subscribe();
Note that in the example above await is not needed as concatMap knows how to deal with a promise which eventWorkflow returns: concatMap converts it to an observable and waits until the observable completes before proceeding with the next event.
So ultimately what I have found is that what I need is more accurately described as a fifo queue or buffer. I need the messages to wait until the previous message is done processing.
I have also pretty certain that rxjs doesn't offer this ( sometimes referred to as backpressure ). So what I have done is to just import a fifo queue and hook it to each subscriber.
I am using concurrent-queue which so far seems to be working pretty well.

How can I complete Observable in RxJS

Let's say we have an Observable:
var observable = Rx.Observable
.fromEvent(document.getElementById('emitter'), 'click');
How can I make it Complete (what will trigger onComplete event for all subscribed Observers) ?
In this present form, you cannot. Your observable is derived from a source which does not complete so it cannot itself complete. What you can do is extend this source with a completing condition. This would work like :
var end$ = new Rx.Subject();
var observable = Rx.Observable
.fromEvent(document.getElementById('emitter'), 'click')
.takeUntil(end$);
When you want to end observable, you do end$.onNext("anything you want here");. That is in the case the ending event is generated by you. If this is another source generating that event (keypress, etc.) then you can directly put an observable derived from that source as an argument of takeUntil.
Documentation:
http://reactivex.io/documentation/operators/takeuntil.html
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/takeuntil.md
What worked for me is using the take() operator. It will fire the complete callback after x number of events. So by passing 1, it will complete after the first event.
Typescript:
private preloadImage(url: string): Observable<Event> {
let img = new Image();
let imageSource = Observable.fromEvent(img, "load");
img.src = url;
return imageSource.take(1);
}
I think what you are looking for is the dispose() method.
from: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables
Notice that the subscribe method returns a Disposable, so that you can unsubscribe to a sequence and dispose of it easily. When you invoke the dispose method on the observable sequence, the observer will stop listening to the observable for data. Normally, you do not need to explicitly call dispose unless you need to unsubscribe early, or when the source observable sequence has a longer life span than the observer. Subscriptions in Rx are designed for fire-and-forget scenarios without the usage of a finalizer. Note that the default behavior of the Observable operators is to dispose of the subscription as soon as possible (i.e, when an onCompleted or onError messages is published). For example, the code will subscribe x to both sequences a and b. If a throws an error, x will immediately be unsubscribed from b.
I found an easier way to do this for my use case, If you want to do something when the observable is complete then you can use this:
const subscription$ = interval(1000).pipe(
finalize(() => console.log("Do Something")),
).subscribe();
The finalize is triggered on complete, when all subscriptions are unsubscribed etc.

Categories