Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
}
like image 574
Informat Avatar asked Jul 04 '19 17:07

Informat


People also ask

Does takeUntil complete the Observable?

The takeUntil operator returns an Observable that emits value from the source Observable until the notifier Observable emits a value. When the notifier emits a value, the TakeUntil completes the Source observable.

Where should takeUntil be placed?

The general rule is for takeUntil to be placed last. However, there are some situations in which you might want want use it as the second-last operator. RxJS includes several operators that emit a value when the source observable to which they are applied completes.

How do you use takeWhile RxJS?

takeWhile operator works in the following way: Subscribe to a source observable. When a new value arrives from a source observable, execute the predicate function. If the function returns truthy value, send the value to the observer.


2 Answers

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.

like image 64
Adrian Brand Avatar answered Oct 16 '22 13:10

Adrian Brand


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.

like image 30
Damian Kociuba Avatar answered Oct 16 '22 14:10

Damian Kociuba