Does overriding a Subscription automatically unsubscribes the previous value? - javascript

I have the following subscription in an angular application:
private _sub: Subscription;
On initialization I subscribe to a firebase get function:
this._sub = this.service.get('database1').subscribe(
data => {
this.ListOfData = data;
}
);
But depending on user input I change database1 to other collections, or use a different method instead of get().
When I override _sub will the previous stream get automatically unsubcribed, or do I have to manually do it before overriding?

Short answer: No it doesn't...I'll share the details on why
Whenever .subscribe() is called, a new instance of a Subscription is created that holds resources that listen to the execution of the Observable. If you call .subscribe() and do not call .unsubscribe(), your Subscription is still utilizing resources. In some cases (depending on the logic associated to the Observable/Subject) it can result in unexpected behavior in your application and it can impact performance. This is why the best practice is to call .unsubscribe() on your Subscription when you are done with it.
There are also functions called RxJS operators that can help you to manage your subscriptions by automatically unsubscribing based on certain criteria.

No, you have to unsubscribe. Sometimes they stay like sleeper cells to damage your application performance. Best practice is unsubscribe.

Related

How to prevent API calls on a minute timer loop when in the process of logging out of a web app?

In my Ionic/Angular app, I have a 60 second timer observable which just emits the current time synced with server time. Each minute I fetch permissions, settings, etc. I pass a token with each request. On logout I revoke the token. Here's a sample of what my logic looks like.
Side note: There's also a feature where a user can "change login type" where they can "become" an administrator, for example, and this process may also trigger a similar circumstance.
this.clientTimeSub = this.timeService.clientTime
.pipe(takeUntil(this.logoutService.isLoggingOut$))
.subscribe(async (latestClientTime) => {
this.clientTime = { ...latestClientTime };
// if client time just rolled over to a new minute, update settings
if (
this.clientTime?.time?.length === 7 &&
this.clientTime?.time?.slice(-1) === '0'
) {
await updateSettings();
await updatePermissions();
// etc
// These functions will:
// (1) make an api call (using the login token!)
// (2) update app state
// (3) save to app storage
}
});
When I am logging out of the app, there's a small time window where I could be in the middle of sending multiple api requests and the token is no longer valid, due to the timer rolling to a new minute just as I was logging out, or close to it. I am then presented with a 401: Unauthorized in the middle of logging out.
My naive solution was to tell this observable to stop propagation when a Subject or BehaviorSubject fires a value telling this observable that it is logging out, you can see this here .pipe(takeUntil(this.logoutService.isLoggingOut$)).
Then, in any of my logout methods, I would use:
logout() {
this.isLoggingOut.next(true);
...
// Logout logic here, token becomes invalidated somewhere here
// then token is deleted from state, etc, navigate back to login...
...
this.isLoggingOut.next(false);
}
In that small time window of logging out, the client timer should stop firing and checking if it's rolled to a new minute, preventing any further api calls that may be unauthenticated.
Is there a way I can easily prevent this issue from happening or is there a flaw in my logic that may be causing this issue?
I appreciate any help, thank you!
First of all, it is not the best way to use async-await along with RXJS. Its because RXJS as a reactive way of functional programming, have its "pipeable" operators so you can kinda "chain" everything.
So instead of having a logic of calculating time in your subscribe callback function you should rather use, for example filter() RXJ operator, and instead of using await-async you can use switchMap operator and inside it, use forkJoin or concat operator.
this.timeService.clientTime
.pipe(
// Filter stream (according to your calculation)
filter((time) => {
// here is your logic to calculate if time has passed or whatever else you are doing
// const isValid = ...
return isValid;
}),
// Switch to another stream so you can call api calls
// Here with "from" we are converting promises to observables in order to be able to use magic of RXJS
switchMap(_ => forkJoin([from(updateSettings), from(updatePermissions)])),
// Take until your logout
takeUntil(this.logoutService.isLoggingOut$)
).subcribe(([updateSettings, updatePermissions]) => {
// Basically your promises should just call API services, and other logic should be here
// Here you can use
// (2) update app state
// (3) save to app storage
})
If you split actions like in my example, in your promises you just call api calls to update whatever you are doing, then when its done, in subscribe callback you can update app state, save to app storage etc. So you can have 2 scenarios here:
Api calls from promises, are still in progress. If you trigger logout in the meanwhile takeUntil will do the thing and you will not update app state etc.
If both Api calls from promises are done, you are in a subscribe callback block and if its just a synchronous code (hopefully) it will be done. And then async code can be executed (your timer can now emit next value, its all about Event Loop in javascript)

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.

Why would I use RxJS interval() or timer() polling instead of window.setInterval()?

Use case: Call a function every minute (60000 ms) that dispatches store action to fetch lastUpdated status of items, which upon response and filtering, updates the store, and updated store is read as an observable and displayed in the view). This needs to happen for as long as the web app is open (so indefinitely).
Currently, I'm using this:
this.refreshDate = window.setInterval(
() => this.store.dispatch(new FetchLastUpdate())
, 60000);
And when view is destroyed/dismounted, I delete the interval as so:
if (this.refreshDate) {
clearInterval(this.refreshDate);
}
Is this efficient/effective, or is it troublesome?
Why would I want to use an RxJS polling strategy like:
interval(60000)
.pipe(
startWith(0),
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
Or
timer(0, 60000)
.pipe(
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
TL;DR: window.setInterval() vs. RxJS timer()/interval()
Conclusion/answers (for ease of research):
There is great benefit to using RxJS functions to set an interval or perform polling, these benefits are explained in the selected answer but also in comments, but it is concluded (by discussions in the comments) that for the very simple requirement defined in the "Use case" section at the beginning of this post, it is unnecessary to use RxJS, and in fact if you are not using RxJS in any other part of your program, do not import it just for this, however in my case, I had already imported and used RxJS elsewhere.
Advantage of RxJS:
Laziness
You can create your Observables and until you call subscribe nothing is happening. Observable = pure function. This gives you more control, easier reasoning and allows for next point...
Composability
You can combine interval/timer with other operators creating custom logic very easily in unified way - for example you can map, repeat, retry, take... etc. see all operators
Error Handling
In case of an error you are responsible for calling clearTimeout/clearInterval - Observables are handling this for you. Resulting in cleaner code and fewer memory leak bugs.
Of course anything you do with Observables you can also do without Observables - but that's not the point. Observables are here to make your life easier.
Also note that interval/timer are not good observable factories for polling because they do not "wait" for your async action to finish (you can end up with multiple async calls running over each other). For that I tend to use defer and repeatWhen like this:
defer(() => doAsyncAction())
.pipe(
repeatWhen(notifications => notifications.pipe(delay(1234)))
);
window.setInterval doesn't care about your callbacks state, it'll execute at the given interval despite the status of the execution of the past callback, and the only way to make it stop and skip is clear the interval or reinitialize it.
On the other hand, RxJS Observable based solutions(interval, timer) allow you to pipe conditional operators (takeWhile, skipWhile for example) which allows you to add a stop or implement a stop-start logic by just flipping a boolean flag, instead of adding complicated logic of clearing the interval, and then recreating it.
And they are observables, you can listen to them all across the application, and attach any number of listeners to it.
Error Handling is better too, you subscribe to all successes, and handle everything in a catch callback.

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?

RXJS Subject, return the same value on every first subscribe

I know I need to use startWith, but still trying to figure out how to use it. If I just do Subject.create().startWith("Some Value), it turns the Subject into a Observable, and I can't use next to emit.
So multiple subscribers should be able subscribe to it. Should be able to call next on the Subject. Going through the docs of Subject.create(), but it's going slow.
Edit:
I got it to work after using the accepted solution. The reason why it wasn't working before was because I put the .next call inside the subscription.
Eg:
observable.subscribe((res) => {
// do something
s.next('another res');
}
This creates an infinite loop, and I think RXJS prevented it? Anyway, I put the next in there for debug purposes. I moved it outside of that subscribe block and now and initial result emits, then when next is called, whatever was inside subscribe emit again.
You should avoid using Subject.create() and use just Subject(). See: Subject vs AnonymousSubject
Just keep a reference to the Subject instance and another reference to the Observable chain you need:
let s = new Subject();
let observable = s.startWith("Some initial message");
observable.subscribe(...);
s.next('whatever');

Categories