This boils down to having a "pausable timer". Once you have that, you can just use the repeat
operator to re-run it whenever it completes:
somePausableTimer$.pipe(
repeat(),
switchMap(() => of('polling time'))
)
If you care about time precision, here's my take on "pausable timer" - I think it's about as precise as you can get with javascript, but without the performance penalty that comes with solutions such as "check the pause state every 10 ms":
import {delay, distinctUntilChanged, filter, map, mapTo, pairwise, repeat,
scan, startWith, switchMap, take, withLatestFrom} from 'rxjs/operators';
import {defer, fromEvent, NEVER, Observable, of} from 'rxjs';
function pausableTimer(timerSpan: number, isActive$: Observable<boolean>): Observable<boolean> {
const activeState$ = isActive$.pipe(
distinctUntilChanged(),
startWith(true, true),
map(isActive => ({
isActive,
at: Date.now()
}))
);
const pauseSpans$ = activeState$.pipe(
pairwise(),
filter(([,curr]) => curr.isActive),
map(([prev, curr]) => curr.at - prev.at)
);
const accumulatedPauseSpan$ = pauseSpans$.pipe(
scan((acc, curr) => acc += curr, 0)
);
return defer(() => {
const startTime = Date.now();
const originalEndTime = startTime + timerSpan;
return activeState$.pipe(
withLatestFrom(accumulatedPauseSpan$),
switchMap(([activeState, accPause]) => {
if (activeState.isActive) {
return of(true).pipe(
delay(originalEndTime - Date.now() + accPause)
);
}
else {
return NEVER;
}
}),
take(1)
);
});
}
The function accepts a timerSpan
in ms (in your case 10000) and an isActive$
observable which emits true
/false
for resume/pause. So to put it together:
const isActive$ = fromEvent(document, 'click').pipe(scan(acc => !acc, true)); // for example
pausableTimer(10000, isActive$).pipe(
repeat(),
switchMap(() => of('polling time'))
).subscribe();