I am using Angular 5 and have subscribed an observable using the subscribe() method. I want to know if only calling the unsubscribe() method on the subscription will be sufficient to cleanup everything, or should I also call remove() method?
code snippet:
`
// somewhere in a method
this.s1 = someObservable.subscribe((value) => {
//somecode
});
// in ngOnDestroy
this.s1.unsubscribe(); // should I also call .remove()
`
.remove remove the subscription from an internal list, but it does not unsubscribe.
.unsubscribe clean up everything, do the unsubscribe and remove the observer from the internal list. (There was a bug (fixed) that didn't remove the observer from the list)
.takeWhile keep alive the subscription whilst a certain situation continues to be false
example:
this.service.method()
.subscribe(res => {
//logic
});
this will never unsubscribe.
this.service.method()
takeWhile(() => this.isAlive) // <-- custom variable setted to true
.subscribe(res => {
//logic
});
ngOnDestroy(){
this.isAlive = false;
}
Automatic unsubscribe when the component is going to be destroyed.
this.s1 = someObservable.subscribe((value) => {
//somecode
});
public yourMethod(){
this.s1.unsubscribe();
}
this subscription will exists and be "alive" until yourFunction is not called.
--
I personally like to use the rxjs operator takeWhile to keep the code clean. In a very big project or single component having multiple subscription it's confusing having (IE) 30 variables: Subscription. So If you are asking when to use the takeWhile operator my answer is: (Taking as example one subscription) -> If you are sure that the unsubscribe needs to be done when the component is destroyed, use takeWhile. If you need to unsubscribe in a certain scenario where the component is still "alive", use the second example I wrote.
Hope to have clarified the argument.
Related
private loadData(): void {
const q = query(collection(this.store, 'users'), orderBy('userName'));
this.unsubscribe = onSnapshot(q, p => {
let users: any[] = [];
p.forEach(x => {
users.push(x.data());
});
this.usersDataSource = users;
});
}
In the above method a listener is being created, and it can be removed the listener by calling -
this.unsubscribe();
On the same page/component, if this method gets called multiple times, does Firebase creates new listener each time? If yes, then what is the best way to remove all those listeners?
It seems your question is around having to remove each listener you register, which indeed you have to do.
While the SDK may deduplicate the listeners to the backend (it may, but I actually haven't checked in a while) that is unrelated to the API exposed to you: the number of calls to subscribe a listener must be balanced with the number of calls to unsubscribe it before the SDK will stop listening for changes on the server.
I typically keep a list of unsubscribe method for each scope of my application that I need to unsubscribe when the scope changes. So for example, if I have a React app, I do this per component. In other frameworks, the route-change logic is usually a good place to unsubscribe listeners from the old route.
I tried to setup a timeout function with rxjs observable inside a angular2 component
this._subscription = Observable.timer(1000).subscribe(() => {
console.log('inside timeout')
})
and unsubscribe it in other method. However the observable never executed
If i change to
let _subscription = Observable.timer(1000).subscribe(() => {
console.log('inside timeout')
})
It works fine. I also tried with
this._subscription=setTimeout(()=>{},1000)
same thing happened. I suspect it's the ngZone bug so I wrap the function inside
this._ngZone.runOutsideAngular(() => {})
but result is the same. Anyone encounter the same issue before? i am using angular 2.2.4
Subscribing to an observable should be done (in most cases) within ngOnInit method.
So now, if you want to unsubscribe from it, you might do it like that:
private onDestroy$ = new Subject<void>();
private stopObs$ = new Subject<void>();
ngOnInit() {
someObs$
// stop the observable if the component is being destroyed
.takeUntil(this.onDestroy$)
// stop the component from another method if you want
.takeUntil(this.stopObs$)
.do(x => console.log(`A value has been received: ${x}`))
.subscribe();
}
callThisMethodToStopListeningToObs() {
this.stopObs$.next();
this.stopObs$.complete();
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
Here someObs$ might be an interval, a timer or whatever observable :).
Turns out i have a clear subscription in onNgInit to clear subscription upon Component initialisation.
NgInit is run after component method in which the observable sits. So it cancels the subscription immediately after it's created. Now i removed the subscription reset, it works fine. What i learnt is your component method can be called by other component before NgInit hook happen
I have this code that checks to see if a user is already signed in in Firebase, if so, use Redux to dispatch an action and update the state to the current auth user.
/**
* check to see if the user has signed in already or not
*/
function initAuth(dispatch) {
return new Promise((resolve, reject) => {
const unsubscribe = firebase.auth().onAuthStateChanged(
authUser => {
dispatch({ type: "INIT_AUTH", payload: authUser });
unsubscribe();
resolve();
},
error => reject(error)
);
});
}
initAuth(store.dispatch)
.then(() => render())
.catch(error => console.error(error));
What I am confused is, why is the unsubscribe() called within the unsubscribe? I know you can do this as in JavaScript recursion, but what's the use here? Thanks!
onAuthStateChanged takes a function as it's only argument. That function is the one that will be invoked whenever the auth state changes. So the code
function printHelloWorld() {
console.log("Hello World")
}
firebase.auth().onAuthStateChanged(printHelloWorld)
Will print "Hello World" to the console, any time the auth state changes. But, at some later time, we want to stop that function from executing anymore, because we've already done whatever we need to. If you're familiar with event listeners, they use a pattern where to remove one, you would call something like removeEventListener. But firebase does not have a offAuthStateChanged or some such. Instead the onAuthStateChanged function returns a function to you that unsubscribes the function you originally gave it. To be clear, it does not return your original function (the one you gave it, so printHelloWorld in this example), but returns you a new function that can be used to remove the original.
So going back to the example:
function printHelloWorld() {
console.log("Hello World")
}
var unsubscribe = firebase.auth().onAuthStateChanged(printHelloWorld)
// ... Sometime later when we are no longer interested in auth changes
unsubscribe();
// From this point forward, when the auth state changes, printHelloWorld will no longer be triggered.
Finally, suppose that you only want to have a function run on auth changes, but only one time. The simplest way to do that would be to have it run once, then unsubscribe it. So the code:
var unsubscribe = firebase.auth().onAuthStateChanged(() => {
console.log("Hello World")
unsubscribe()
})
means that the first time auth state changes, we will log the string, then immediately unsubscribe from further changes. So by calling the unsubscribe from within the function itself, we are just saying, run one time, then remove yourself.
Also, note that you can call unsubscribe at the beginning or end of the function, it doesn't matter. The entire function body will execute, just like any other. So calling unsubscribe won't halt the execution of the remainder of the function, or anything like that.
This is why things like
var unsubscribe = firebase.auth().onAuthStateChanged(() => {
unsubscribe()
// Lots of other code here...
});
is such a common pattern.
If you want to listen for the changes in the auth status of the user just one time you have to do it this way:
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if(unsubscribe) {
unsubscribe();
}
}
It seems the listener runs twice, the first time when is created, and second time when the user actually changes his status. In the first time the unsubscribe is not defined so you to check that is defined before run it.
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?
Just as the title says, in Angular 2, is there any way to check if source is already subscribed? Because I have to check it before using
this.subscription.unsubscribe();
This is my code:
this.Source = Rx.Observable.timer(startTime, 60000).timeInterval().pluck('interval');
this.Subscription = this.Source
.subscribe(data => { something }
and then I want to be sure that it is subscribed before calling unsubscribe()
It seems you can check whether the subscription is closed with
this.subscription.closed
indicate whether this Subscription has already been unsubscribed.
I had a similar case, I had a if condition with one optional subscribe:
if (languageIsSet) {
// do things...
} else {
this.langSub = this.langService.subscribe(lang => {
// do things...
});
}
If you want to call unsubscribe safely (you don't know if is subscribed or not), just initialize the subscription with EMPTY instance:
private langSub: Subscription = Subscription.EMPTY;
Now you can unsubscribe without errors.
You can check if Subject has observers because it has a public property observers.
With Observables you can't because they don't typically have arrays of observers. Only if you've multicasted them via a Subject with the multicast() operator for example.
Maybe if you could describe your use case in more detail I'll be able to give you better advice.
const source = ...;
let subscribed = false;
Rx.Observable.defer(() => {
subscribed = true;
return source.finally(() => { subscribed = false });
})
As I can see in your code you always create a subscription.
So if you created subscriptions object it means subscription exists and you can unsubscribe.
It Still a bit not clear why you need to check is any subsection exist
Btw. unsubscribe() method checking is subscription closed or not.
Subscription is closed if somebody called unsubscribe() or observable is compleated