简体   繁体   中英

RXJS - Using both take(1) and takeUntil()

After searching online, I see that people generally use one or the other and not both. Is it possible to use both? Is it good or bad practice?

Edit

I am not asking what take(1) or takeUntil is used for. Rather I am asking if it is possible to have both effects of take(1) and takeUntil. I understand that take(1) will take only the first value and end the subscription. However, it will not end the subscription if I never receive the event. I also understand that this use of takeUntil will always clean up the subscription when the component is no longer active as long as I trigger the unsubscribeSubject in the destroy. However, it will not free up that subscription after I receive the first value and will exist for the entire time the component is active.

What I want is a way to free the subscription after the first value as well as prevent memory leaks when the component is no longer active if not value is received. Specifically in a scenario where you have many subscriptions. This is because when you have many subscriptions, it is handy to have a single subject that can clean up all your subscriptions when the component is no longer needed.

ngAfterViewInit(){
    //if first value comes, we process it and unsubscribe
    this.myService.GetOneTimeObservable()
        .pipe(take(1))
        .pipe(takeUntil(this.unsubscribeSubject))
        .subscribe(this.fooOT.bind(this))

    //other subscriptions (assume many)
    this.myService.GetLongLifeObservable1()
        .pipe(takeUntil(this.unsubscribeSubject))
        .subscribe(this.foo1.bind(this))

    this.myService.GetLongLifeObservable2()
        .pipe(takeUntil(this.unsubscribeSubject))
        .subscribe(this.foo2.bind(this))

    this.myService.GetLongLifeObservable3()
        .pipe(takeUntil(this.unsubscribeSubject))
        .subscribe(this.foo3.bind(this))
}

ngOnDestroy(){
    //Ideally cleans all subscriptions, including the OneTime if no value is received
    this.unsubscribeSubject.next();
    this.unsubscribeSubject.complete();
}

take(1) is not guaranteed to cancel the observable because it may not have emitted before the component is destroyed. The takeUntil guarantees that the observable is not a memory leak. If the observable is taking a long time to emit before the component is destroyed and you only have a take(1) and you move on to another component the subscription is still listening and will fire even though the component is no longer active.

The only reason to use take(1) is if the subject might emit more than once and you only want the first value, takUntil is enough to make sure that there is not a memory leak.

Shortly: yes, it's possible to use both. You can try by yourself using this example:

import { fromEvent, timer } from 'rxjs'; 
import { map, takeUntil, take } from 'rxjs/operators';

const source = fromEvent(document, 'click');
const destruct = timer(5000);

source.pipe(
  takeUntil(destruct), //the order of take/takeUntil doesn't matter
  take(1),
).subscribe(
  () => console.log('click'),
  () => console.log('error'),
  () => console.log('complete')
); 

Observable is completed on first click OR destruct event (here simulated by timer). In my opinion it is not a bad practice, but I'm not specialist.

If your thought process is: "I know I could get multiple emitted values but just want the first one." and "I may get no values and want the subscription unsubscribed on component destroy." then create a class variable to store the subscription and unsubscribe from that on destroy.

private oneTimeSubscription: Subscription;

ngAfterViewInit(){
  this.oneTimeSubscription = this.myService.GetOneTimeObservable()
    .pipe(take(1))
    .subscribe(this.fooOT.bind(this))
}

ngOnDestroy(){
  this.oneTimeSubscription.unsubscribe();
}

or you could unsubscribe in-line:

ngAfterViewInit(){
  const oneTimeSubscription: Subscription = this.myService.GetOneTimeObservable()
    .pipe(take(1))
    .subscribe(this.fooOT.bind(this, oneTimeSubscription))
}

fooOT(subscription: Subscription): void {
  subscription.unsubscribe();
  // rest of your code here...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM