3

I have the following login function that I want to use just once to login as oppose to sit there an listen for data forever. So I am looking for the right time to call the unsubscribe method. At the moment, I am calling the unsubscribe method right after I get a result or error. This would make sense as the observable is no longer required.

However... what happens if I have a really slow internet and here is what happens.

The code executes the observable and waits for a data to come back from firebase (Say for 1 minute for arguments sake). During this wait period, say if someone modifies this entry in firebase, I believe firebase will think, "hey someone is still listening and there is a change, so I better emmit the update to this person"

So at this point, I would be waiting for two sets of data to come back, the first one and the updated one.

So would I get two data printed out of the console or will this not happen?

onLogin() {
    // loginWithEmailPassword returns firebase.promise<AuthState>
    this.userService.loginWithEmailPassword(this.loginForm.value.email, this.loginForm.value.password)
    .then(data => {

        // if the user is logged in, go and retreive user information
        // getUserInformation returns Observable<any>
        let subscription_getUserInformation = this.userService.getUserInformation(data.uid)
        .subscribe(result => {

            // user information received
            console.log(result)

            // remove Subscription
            subscription_getUserInformation.unsubscribe();

        }, error => {
            // something went wrong
            subscription_getUserInformation.unsubscribe();
        })

    }, error => {
        // something went wrong
    });
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
user172902
  • 3,541
  • 9
  • 32
  • 75

1 Answers1

2

Your subscriber will be notified only once, as you are unsubscribing in the subscriber's next function.

However, the code is more complicated than it needs to be, as subscribers to an observable are automatically unsubscribed whenever the observable completes or errors.

If you only want the first emitted value from an observable, you can use the first operator (which is equivalient to take(1)):

import 'rxjs/add/operator/first';

onLogin() {
  this.userService
    .loginWithEmailPassword(
      this.loginForm.value.email,
      this.loginForm.value.password
    )
    .then(data => this.userService
      .getUserInformation(data.uid)
      .first()
      .subscribe(
        result => { console.log(result); },
        error => { console.log(error); }
      )
    )
    .catch(error => { console.log(error); });
}

Using that operator will ensure the composed observable completes after the first emitted value, at which point the subscriber will be automatically unsubscribed - so there is no need for explicit unsubscription/clean up.

cartant
  • 57,105
  • 17
  • 163
  • 197
  • Great solution. it works perfectly. I am abit confused now though @cartant, I thought the whole purpose of subscribing to observable is that it just sits there and listen forever until you unsubscribe it (One of the big advantage over Promise I believe). Why did you say that "observable are automatically unsubscribed whenever the observable completes or errors". Before when I didnt have unsubscribe, the subscription does not go away automatically and just sits there and listen forever. I believe your code work because first() kills off the subscription after first return? – user172902 Feb 04 '17 at 06:19
  • 1
    The `first` operator ensures the composed observable completes after the first emitted value. Without that operator, the source observable could have emitted further values - which was what would have been happening when you were not unsubscribing and had not used the `first` operator. Regarding the automatic unsubscription on completion/error, that's just the way they behave. See the *Subscribing and Unsubscribing* section of the [*Observable Contract*](http://reactivex.io/documentation/contract.html). – cartant Feb 04 '17 at 06:25
  • 1
    You might also find this an interesting read: [*RxJS: Don’t Unsubscribe*](https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87) – cartant Feb 04 '17 at 06:26
  • That clears things up. Does that mean services like firebase where if I query for a child will never send a "completion" status to me (as it is designed to keep an eye out). Hence it the subscription will never terminate (if no error occurs) unless done manually or uses operator like "first()". Thanks for the link too! – user172902 Feb 04 '17 at 06:33
  • 1
    Yep. If you are using something like an AngularFire2 list or object observable, they don't complete; they just re-emit arrays or objects if the database changes. Sometimes that's the behaviour you want, but if you only want one emitted value, use `first` to complete the observable and automatically unsubscribe. – cartant Feb 04 '17 at 06:38
  • Many Thanks! Initially I was thinking on the wrong line. I was thinking "completion" as similar to hardware programming where after a set of data is received, an "end" status is send so you know that is a complete set of data. Such as "Hello World \n" (a very bad example). So I would receive completion every time I receive something new. Leaning new things everyday – user172902 Feb 04 '17 at 06:44