I created a class which sets up a pausable RxJS Observable
using the interval
operator:
export class RepeatingServiceCall<T> {
private paused = false;
private observable: Observable<T>;
constructor(serviceCall: () => Observable<T>, delay: number) {
this.observable = interval(delay).pipe(flatMap(() => (!this.paused ? serviceCall() : NEVER)));
}
setPaused(paused: boolean) {
this.paused = paused;
}
getObservable() {
return observable;
}
}
This seems to work fine, but the problem I am trying to solve is that I want the timer to reset when unpaused. So, let's say that the interval
time is 10 seconds and 5 seconds after the last time the interval
emitted, setPaused(false)
is called. In that scenario, I want it to emit immediately and then restart the timer.
Would something like that be an easy thing to add?
RxJS interval() operator is a creation operator used to create an observable that emits a sequence of integers every time for the given time interval on a specified SchedulerLike. It emits incremented numbers periodically in time.
takeUntil passes values from the source observable to the observer (mirroring) until a provided observable known as the notifier emits its first value. The operator subscribes to the source observable and begins mirroring the source Observable. It also subscribes to the notifier observable.
Example 1: Emit sequence of values at 1 second interval ( StackBlitz| jsBin| jsFiddle) 1 // RxJS v6+ 2 import{interval }from'rxjs'; 3 4 //emit value in sequence every 1 second 5 constsource =interval(1000); 6 //output: 0,1,2,3,4,5....
medium.com/js-in-action/… If you use timer instead of interval, and set the initial delay to 0, then your interval will fire immediately. You can use takeUntil operator to prevent the interval to run always, and repeatWhen operator to restart it whenever you want:
From official documentation, repeatWhen () is deprecated in RxJs of v7 and will be removed in future version, and repeat () is a replacement of it.
If a number, is the time to wait before starting the interval. If a Date, is the exact time at which to start the interval. The delay between each value emitted in the interval.
If you use timer
instead of interval
, and set the initial delay to 0
, then your interval will fire immediately.
You can use takeUntil
operator to prevent the interval to run always, and repeatWhen
operator to restart it whenever you want:
import { Observable, Subject, timer } from 'rxjs';
import { repeatWhen, switchMap, takeUntil } from 'rxjs/operators';
export class RepeatingServiceCall<T> {
readonly observable$: Observable<T>;
private readonly _stop = new Subject<void>();
private readonly _start = new Subject<void>();
constructor(serviceCall: () => Observable<T>, delay: number) {
this.observable$ = timer(0, delay)
.pipe(
switchMap(() => serviceCall()),
takeUntil(this._stop),
repeatWhen(() => this._start)
);
}
start(): void {
this._start.next();
}
stop(): void {
this._stop.next();
}
}
Here is a working StackBlitz example.
P.S.: Getters and setters are working different in typescript. So you do not need classic getter concept, you can just make the attribute public
and readonly
.
You can achieve the behavior you are describing with the following snippet:
const delay = 1000;
const playing = new BehaviorSubject(false);
const observable = playing.pipe(
switchMap(e => !!e ? interval(delay).pipe(startWith('start')) : never())
);
observable.subscribe(e => console.log(e));
// play:
playing.next(true);
// pause:
playing.next(false);
playing
Observable emits true
, the switchMap
operator will return a new interval
Observable.startWith
operator to emit an event immediately when unpausing.true
.StackBlitz Example
Yet another approach with a switchMap
:
const { fromEvent, timer } = rxjs;
const { takeUntil, switchMap, startWith } = rxjs.operators;
const start$ = fromEvent(document.getElementById('start'), 'click');
const stop$ = fromEvent(document.getElementById('stop'), 'click');
start$.pipe(
startWith(void 0), // trigger emission at launch
switchMap(() => timer(0, 1000).pipe(
takeUntil(stop$)
))
).subscribe(console.log);
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<button id="start">start</button>
<button id="stop">stop</button>
And a simpler one, that merge
s start and stop Observables to switch off them:
const { fromEvent, merge, timer, NEVER } = rxjs;
const { distinctUntilChanged, switchMap, mapTo, startWith } = rxjs.operators;
const start$ = fromEvent(document.getElementById('start'), 'click');
const stop$ = fromEvent(document.getElementById('stop'), 'click');
merge(
start$.pipe(mapTo(true), startWith(true)),
stop$.pipe(mapTo(false))
).pipe(
distinctUntilChanged(),
switchMap(paused => paused ? timer(0, 1000) : NEVER)
)
.subscribe(console.log);
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<button id="start">start</button>
<button id="stop">stop</button>
And another, even wierder approach, using repeat()
:
const { fromEvent, timer } = rxjs;
const { take, concatMap, takeUntil, repeat } = rxjs.operators;
const start$ = fromEvent(document.getElementById('start'), 'click');
const stop$ = fromEvent(document.getElementById('stop'), 'click');
start$.pipe(
take(1),
concatMap(()=>timer(0, 1000)),
takeUntil(stop$),
repeat()
).subscribe(console.log);
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<button id="start">start</button>
<button id="stop">stop</button>
Just wanted to join this party :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With