user10239144
user10239144

Reputation:

How to stop the share observable in rxjs

I would like to stop observable, which one was created by share operator.

My service:

private timer = interval(1000).pipe(take(60), share());
private timerStarted = false;

startTimer() {
  this.timer.pipe(
    tap(() => this.timerStarted = true),
    finalize(() => this.timerStarted = false)
  ).subscribe();
}

getTimer() {
  return this.timer;
}

getTimerStarted() {
  return this.timerStarted;
}

My component:

private destroy$ = new Subject<boolean>();

ngOnInit() {
  if (this.timerService.getTimerStarted()) {
    this.timerService.getTimer().pipe(
      takeUntil(this.destroy$),
    ).subscribe();
  }
}

onStartTimer() {
  this.timerService.startTimer();
  this.timerService.getTimer().pipe(
    takeUntil(this.destroy$),

  ).subscribe();
}

ngOnDestroy() {
  this.destroy$.next(true);
}

I tried add to service stop method:

stopTimer() {
        this.timer.unsubscribe();
    }

And use it in ngOnDestroy:

ngOnDestroy() {
  this.destroy$.next(true);
  this.timerService.stopTimer();
}

I would like stop the creating value in the background.

Upvotes: 1

Views: 1103

Answers (2)

bryan60
bryan60

Reputation: 29335

you need to not create the subscription in your service, it's unclear why you're doing this. share() will naturally terminate once it has no subscribers left, but in the service, you're ensuring one subscriber always exists. just do this instead:

export class TimerService {
  timer$ = interval(1000).pipe(
             take(60), 
             tap(() => this.timerStarted = true), 
             finalize(() => this.timerStarted = false),
             share());
  private timerStarted = false;

  getTimerStarted() {
    return this.timerStarted;
  }
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  providers: [TimerService]
})
export class AppComponent  {
  name = 'Angular';

  sub;
  constructor(private timer: TimerService) {
    this.sub = this.timer.timer$.subscribe(v => console.log(v));
    setTimeout(() => this.sub.unsubscribe(), 4000)
  }
}

in the logs you'll see that the timer emits 4 times then stops after the timeout unsubscribes. You could just as easily unsubscribe in onDestroy hook or use the destroy$ emission method. both will have the same effect.

blitz: https://stackblitz.com/edit/angular-9a9bkl?file=src/app/app.component.ts

edit based on comments:

since you do want to keep a subscription alive between components, you need to modify your service to allow you to end that subscription:

private timer = interval(1000).pipe(take(60), takeUntil(stopTimer$), share());
private timerStarted = false;
private stopTimer$ = new Subject();

startTimer() {
  this.timer.pipe(
    tap(() => this.timerStarted = true),
    finalize(() => this.timerStarted = false)
  ).subscribe();
}

stopTimer() {
  this.stopTimer$.next();
}

this method will end the service level subscription and all others because the takeUntil is part of the timer pipe itself.

Upvotes: 1

iams0nus
iams0nus

Reputation: 492

Call the complete method on the Unsubscribe Subject. Then the observable should automatically be destroyed when the ngOnDestroy lifecycle calls.

ngOnDestroy() { this.destroy$.next(true);       this.destroy$.complete();}

Edit:

For the timer subscription store it a property within the service

this.subscription = <your observable>. subscribe()

stopTimer(){ this.subscription.unsubsribe();}

Then in the onDestroy you can call the stopTimer method.

Upvotes: 1

Related Questions