5

I have a simple Rxjs timer that keeps going until a notifier emits something, very basic till here.

enum TimerResult = {
    COMPLETE,
    ABORTED,
    SKIPPED
};

_notifier: Subject<TimerResult> = new Subject();
notifier$: Observable<TimerResult> = this._notifier.asObservable();

simpleTimer$ = interval(1000);

startTimer(): Observable<number> <-- **here I want a timerResult** {
  return simpleTimer$.pipe(
      tap(()=>doSomethingBeautifulWhileRunning),
      takeUntil(this.notifier$)
   )

}

What I'd like to achieve is to get the value emitted by notifier as a result.

I don't need intermediate values , I need only to know when it completes and with what result.


simpleTimer$.pipe(
   tap(()=>doSomethingBeautifulWhileRunning),
   last(),
   takeUntil(this.notifier$)
).subscribe((result)=>{
   // Here, of course, I get the last value 
   // I need instead the value coming from notifier$
});

I tried a lot of possible solutions with Rx operators but none of them worked as expected. The only one I found that produces an acceptable result (but imho very very dirty) was this:

startTimer(): Observable<TimerResult>{
    simpleTimer$.pipe(...).subscribe(); <-- fire the timer, automatically unsubscribed by takeUntil
    return this.notifier$.pipe(first());
}

What's the best "Rx" way to obtain this? I hope I have been clear enough, any help is super appreciated :)

3 Answers3

1

You can merge the notifier with the original stream and get the result from there try the example below.

const notifier = timer(5000).pipe(mapTo("done"));

merge(interval(1000)
  .pipe(takeUntil(notifier)),
   notifier 
  )
  .subscribe(console.log,null,
   _=>console.log('complete') 
    );
Fan Cheung
  • 10,745
  • 3
  • 17
  • 39
  • Thanks! Your example works like a charm, I just added a "last" operator in order to get only the last value (which is emitted by notifier). It was one of the solutions I tried, but in my specific scenario it didn't work, it simply remained pending forever (even if timer was correctly terminated). Now I figured out why! Since I'm using a `Subject`, I need also to call the "complete()" method in order to receive the value in `merge` operator – Federico Rimembrana Apr 29 '20 at 10:20
1

You can also use combineLastest

  startTimer(): Observable<any> {
    const timer$= this.simpleTimer$.pipe(
      tap((res)=>console.log(res)),
      takeUntil(this.notifier$)
    );
    return combineLatest(timer$,this.notifier$)
  }

//and use:
this.startTimer().subscribe(([timer,action])=>{
  console.log(timer,action)
})

see stackblitz

Eliseo
  • 50,109
  • 4
  • 29
  • 67
0

I think you can just merge:

simpleTimer$.pipe(
  tap(() => doSomethingBeautifulWhileRunning),
  last(),
  merge(this.notifier$.pipe(take(1)),
  takeUntil(this.notifier$),
).subscribe((result)=>{
   // Here, of course, I get the last value 
   // I need instead the value coming from notifier$
});

I'm not sure it's going to work in this order however. Maybe you'll have to switch merge and takeUntil.

martin
  • 93,354
  • 25
  • 191
  • 226