I think this should work:
const newSubscription = new Subject();
const covidCases$ = interval(60 * 1000).pipe(
takeUntil(newSubscription),
repeat(),
switchMap(() =>
this.covidService.getCovidCases().pipe(
/* ... */
),
),
takeUntil(this.stopPolling),
shareReplay(1),
src$ => defer(() => (newSubscription.next(), src$))
);
I replaced timer(1, 60 * 1000) + retry()
with interval(60 * 1000)
.
My reasoning was that in order to restart the timer(the interval()
), we must re-subscribe to it. But before re-subscribing, we should first unsubscribed
from it.
So this is what these lines do:
interval(60 * 1000).pipe(
takeUntil(newSubscription),
repeat(),
/* ... */
)
We have a timer going on, until newSubscription
emits. When that happens, takeUntil
will emit a complete notification
, then it will unsubscribe from its source(the source produced by interval
in this case).
repeat
will intercept that complete notification
, and will re-subscribe to the source observable(source = interval().pipe(takeUntil())
), meaning that the timer will restart.
shareReplay(1)
makes sure that a new subscriber will receive the latest emitted value.
Then, placing src$ => defer(() => (newSubscription.next(), src$))
after shareReplay
is very important. By using defer()
, we are able to determine the moment when a new subscriber arrives.
If you were to put src$ => defer(() => (console.log('sub'), src$))
above shareReplay(1)
, you should see sub
executed logged only once, after the first subscriber is created.
By putting it below shareReplay(1)
, you should see that message logged every time a subscriber is created.
Back to our example, when a new subscriber is registered, newSubscription
will emit, meaning that the timer will be restarted, but because we're also using repeat
, the complete notification won't be passed along to shareReplay
, unless stopPolling
emits.
StackBlitz demo.