0

I'm writing an angular15 app with a youtube player component in it, i'm trying to work with rxjs but i think that i have one issue that i got wrong, the mergeMap. i'm really new to rxjs so sorry for any mistakes

I have 2 subscriptions, one for if youtube library as finished loading, and the other if the youtube player is ready.

first lets look just at the interval:

this.YTSubscription=interval(100).pipe(
      exhaustMap((x, y)=>{
        this.currentTimeSubject.next(this.player.getCurrentTime());
        this.isPlayingSubject.next(this.player.getPlayerState() === YT.PlayerState.PLAYING);
        this.isMutedSubject.next(this.player.isMuted());
        this.volumeSubject.next(this.player.getVolume());
        return of(true);
      }),
    ).subscribe({next: (data )=>{
      },
      error: (err)=> {
        this.YTSubscription?.unsubscribe();
      }
    });

this works fine, it runs in intervals on 100ms and i use exhaustMap to make sure that the next iteration will be executed only if the previous one completed in case when i'll add more calculations it may take more than 100 ms.

next i want in the interval to check if youtube is loaded, for that i have the observable isYouTubeLoaded, so i tried using mergeMap for this.. i guess this is not the right way? but it still worked:

this.YTSubscription=interval(100).pipe(
      mergeMap(x => this.isYouTubeLoaded),
      exhaustMap((x, y)=>{
        if (!x) {
          return of(false);
        }
      ...

now x inside exahustMap contains the isYouTubeLoaded and this does the job.

now i have another observable that i want to check and only if both of them are true to run the interval, if not to wait for the next iteration, this is where i get lost because if i add another mergeMap i can't see both values in exhaustMap.

so from reading some more i assume that i'm not supposed to use mergeMap at all, maybe filter ? but i still have no clue how to do that with 2 observables.

any ideas?

ufk
  • 30,912
  • 70
  • 235
  • 386
  • The first `exhaustMap` doesn't have any effect, because the returned observable `of(true)` will complete immediately. If you intend to wait for an async operation you have to return an appropriate observable that represents this async operation. If you have more than 100ms of synchronous calculationsn the program will be busy for this period of time anyway. There can't be a timer emission in the middle of executing synchronous code. I believe you have a slight misunderstanding there. – Lukas-T Dec 07 '22 at 07:26
  • Also, you don't need to unsubscribe in the case of an error the pipe will end and be cleaned up in this case, just as if it completed. That doesn't mean you shouldn't handle the error, just that calling `unsubscribe` isn't needed there. – Lukas-T Dec 07 '22 at 07:28
  • @churill - thanks, things are not that clear for me yet – ufk Dec 08 '22 at 16:55

2 Answers2

1

I'm not entirely sure, what you want to do, but I'll try to answer this part of your question:

now i have another observable that i want to check and only if both of them are true to run the interval, if not to wait for the next iteration, this is where i get lost because if i add another mergeMap i can't see both values in exhaustMap.

combineLatest([src1, src2]).pipe(       // check both
  filter(([ok1, ok2]) => ok1 && ok2),   // only if both are true
  switchMap(() => timer(...)            // run the timer
).subscribe(...);
Lukas-T
  • 11,133
  • 3
  • 20
  • 30
  • i should have started with combielatest, i tried to adding combieLatestWith inside the pipe which complicated things. adding my final answer – ufk Dec 14 '22 at 18:17
0

@churill really helped, in the end i need two pipes and not 3 but the implementation is the same, still marking his answer as the correct one, just showing here the resulting code:

 this.YTSubscription=combineLatest([interval(100), this.isYouTubeLoaded]).pipe(
      map(([intr, loaded])=>(loaded)),
      filter((loaded)=> (loaded)),
      exhaustMap(()=>{
        try {
          if (this.player.getPlayerState() === YT.PlayerState.UNSTARTED) {
            return of(false);
          }
        } catch (e) {
          return of(false);
        }
        this.currentTimeSubject.next(this.player.getCurrentTime());
        this.isPlayingSubject.next(this.player.getPlayerState() === YT.PlayerState.PLAYING);
        this.isMutedSubject.next(this.player.isMuted());
        this.volumeSubject.next(this.player.getVolume());
        return of(true);
      }),
    ).subscribe({next: (isUpdated)=>{
      },
      error: (err)=> {
        console.error(err);
      }
    });
ufk
  • 30,912
  • 70
  • 235
  • 386