Let's say I have two classes, where you can observe over some observables.
First example, with public subject:
class EventsPub {
public readonly onEnd = new Subject<void>();
}
Second example, with private subject and registering method:
class EventsPriv {
private readonly endEvent = new Subject<void>();
public onEnd(cb: () => void): Subscription {
return this.endEvent.subscribe(cb);
}
}
The first example is somehow unsafe because anyone can call eventsPub.endEvent.next()
from outside the class and introduce side effects, however, comparing to example 2 It allows for pipes, which is a big plus since developers can for ex. register only for the first event with eventsPub.onEnd.pipe(first()).subscribe(cb)
.
The second example also allows for one-time subscription but requires more code and ugly unsubscribing.
const subscription = eventsPriv.onEnd(() => {
// logic..
subscription.unsubscribe()
});
From your point of view, which is the best way to go? Or maybe there is a better solution?
An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast. A Subject is like an Observable, but can multicast to many Observers.
RxJS pushes notifications to subscribers. Subscribers don't hold references to the observables; it's the other way around. So, unless you've explicitly created a subscriber that holds a reference to the Subject - via a closure or some other mechanism - there's no need to call complete for garbage-collection purposes.
Use a Subject when…When you need multiple subscribers and care that all the subscribers are getting their new values simultaneously, you need a Subject . Use a BehaviourSubject when you need the last given value. Use a ReplaySubject when you need more than the last given value (For example, the previous five values).
The one and major difference between observable and the subject is that observables are unicast (each subscribed Observer owns an independent execution of the Observable) while the subject is multicast which means it is like an event emitter, it maintains the registry of listener(s) and updates each listener every time ...
This is based a lot on my personal preference but I'd do it like this:
class EventsPriv {
private readonly endEvent = new Subject<void>();
get endEvent$(): Observable<void> {
return this.endEvent;
}
}
So inside the class I'll use endEvent
while I can still use it eg. in a template with obj.endEvent$ | async
and from the outside it behaves like an Observable.
Note, that in fact I'm returning the same instance of Subject
. The only thing that restricts the outside world from misusing it with obj.endEvent$.next()
are Typescript's type guards. If I was using just JavaScript or if I typecasted it to any
I could call next
.
This is actually the recommended way of exposing Subject
s instead of using the asObservable()
operator. You can notice that this is used everywhere internally in RxJS 5. For example if you look at repeatWhen
synopsys:
public repeatWhen(notifier: function(notifications: Observable): Observable): Observable
You can see that the notifier
function receives an Observable as a parameter (you can see it in the code here as well https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L29).
But if you look into the code where the function is called you'll see they are in fact passing a Subject
and not an Observable
: https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L114-L115.
This has been discussed on RxJS GitHub page and reasons for this are performance and that the Typescript type guards are sufficient. You can read more in these discussions:
https://github.com/ReactiveX/rxjs/pull/2408
https://github.com/ReactiveX/rxjs/issues/2391
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With