As per most of the blogs we don't need to explicitly unsubscribe the subjects once we call complete. I tried to subscribe the subject after calling complete on it. The complete call back was still executed.
let s = new Subject();
s.complete();
s.subscribe(
() => {
console.log("next");
},
() => {},
() => {
console.log("complete");
}
);
Output: complete
Why subscribing to subject is allowed after it is completed?
What happens in this case is that first, a 'complete' notification is dispatched, and immediately afterwards, the subscription is unsubbed. So it still holds that you don't have to unsub manually.
If you are wondering what's the use of subscribing in the first place, you can think of an example where we apply isEmpty operator to the subject, and do something depending on the value nexted by this operator.
Related
I am new to Angular and I am working on fixing something that is written in Angular 2. In the method we have a call to to observable method after that we are assigning a value to a variable. I need the obersavable method to finish execution before assigning the value. Given below is the code for your reference.
Method1(): void {
this.Service.getData()
.subscribe(
res => {
},
error => this.errorMessage = <any>error);
this.gotData = true;
}
So, As shown above, I need to assign true to gotData variable after the getData Observable method is finished execution. But gotData is getting assigned true even before we actually get the data.
Please help me who to wait until the observable method is finished execution. Thank you !
Sounds like you need to read up on Observables and Subscriptions.
Angular Docs
Alternatively, the subscribe() method can accept callback function definitions in line, for next, error, and complete handlers. For example, the following subscribe() call is the same as the one that specifies the predefined observer:
In your case the callback you have named res will be called after your observable method is finished.
So your code could be:
Method1(): void {
this.Service.getData()
.subscribe(
res => {
this.gotData = true;
},
error => this.errorMessage = <any>error
);
}
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.
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'm trying to do something that feels like it should be straightforward, but is proving surprisingly difficult.
I have a function to subscribe to a RabbitMQ queue. Concretely, this is the Channel.consume function here: http://www.squaremobius.net/amqp.node/channel_api.html#channel_consume
It returns a promise which is resolved with a subscription id - which is needed to unsubscribe later - and also has a callback argument to invoke when messages are pulled off the queue.
When I want to unsubscribe from the queue, I'd need to cancel the consumer using the Channel.cancel function here: http://www.squaremobius.net/amqp.node/channel_api.html#channel_cancel. This takes the previously returned subscription id.
I want to wrap all of this stuff in an Observable that subscribes to the queue when the observable is subscribed to, and cancels the subscription when the observable is unsubscribed from. However, this is proving somewhat hard due to the 'double-asynchronous' nature of the calls (I mean to say that they have both a callback AND return a promise).
Ideally, the code I'd like to be able to write is:
return new Rx.Observable(async (subscriber) => {
var consumeResult = await channel.consume(queueName, (message) => subscriber.next(message));
return async () => {
await channel.cancel(consumeResult.consumerTag);
};
});
However, this isn't possible as this constructor doesn't support async subscriber functions or teardown logic.
I've not been able to figure this one out. Am I missing something here? Why is this so hard?
Cheers,
Alex
The created observable does not need to wait for the channel.consume promise to resolve, as the observer (it's an observer that's passed, not a subscriber) is only called from within the function you provide.
However, the unsubscribe function that you return will have to wait for that promise to resolve. And it can do that internally, like this:
return new Rx.Observable((observer) => {
var consumeResult = channel.consume(queueName, (message) => observer.next(message));
return () => {
consumeResult.then(() => channel.cancel(consumeResult.consumerTag));
};
});
I am trying to poll a REST API to update a data table which is working fine with the following code:
pollData(url, interval) {
return Rx.Observable.interval(interval)
.mergeMap(() => this.http.get(url));
}
// get data
this.dataService.pollData(this.url, this.updateInterval)
.subscribe(
data => console.log(data),
err => console.log(err),
() => console.log('done'));
The problem is that error and complete never get called. Any suggestions to get this working with onError and onCompete would be greatly appreciated. Thanks!
About the onComplete call on the observer, it will be effected only when the source observable finishes. This means when the observable returned by pollData completes. As you are currently polling with no exit condition, then naturally your observable never completes.
To have this observable complete, you need to come up with an exit condition :
timeout (for instance, poll for X seconds, then stop polling)
number of polls
pollData-based condition (for instance, if no changes detected after X consecutive polling)
external completion signal
any other condition which makes sense to your use case
All these conditions are easy to implement with RxJS through they will require you to update the code of the pollData function.
For instance for the external completion signal, you could write :
// defining somewhere the subject for signalling end of polling
stopPollingS = new Rx.Subject();
// somehow pass this subject as a parameter of the polling function
pollData(url, interval, stopPollingS) {
return Rx.Observable
.interval(interval)
.mergeMap(() => this.http.get(url))
.takeUntil(stopPollingS);
}
// somewhere in your code when you want to stop polling
stopPollingS.onNext(true);
About the onError call on the observer, , I am not sure I get what is happening. Have you tried provoking an error and check the onError handler of your observer is indeed called? If there is no error, it is quite obvious that the onError will not be called.
Just in case anyone was wanting to know how I went about solving this problem and implemented the functionality that was required. Basically I just needed to wrap the observable in another and return the error as a data.
initiatePolling(url, interval) {
var http = this.http;
return Rx.Observable.create(function (observer) {
// initial request (no delay)
requestData();
var timerId = setInterval(requestData, interval);
function requestData() {
var subscription = http.get(url).timeout(20000)
.subscribe(
result => {
observer.next(result);
subscription.unsubscribe();
},
err => {
observer.next(err);
subscription.unsubscribe();
},
() => {
subscription.unsubscribe();
});
}
return function () {
observer.complete();
window.clearInterval(timerId);
}
});
}