I have done a small timer, with 3 buttons:
start stop pause/resume (which I haven't done yet)
here is my code so far:
class StopwatchWidgetComponent {
private readonly _start$: Subject<void> = new Subject();
private readonly _stop$: Subject<void> = new Subject();
private readonly _paused$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
readonly counter$: Observable<number>;
constructor() {
this.counter$ = this._start$.pipe(
startWith(0), // trigger emission at launch
switchMap(() => timer(0, 1000).pipe(
takeUntil(this._stop$)
))
);
// Subscribe done later
}
start(): void {
this._start$.next();
}
stop(): void {
this._stop$.next();
}
togglePause(): void {
this._paused$.next(!this._paused$.getValue());
}
}
However, I didn't manage to use the _paused$
subject properly so far. By looking at github, I found a few topics about this subject:
https://github.com/ReactiveX/rxjs/issues/1542
but I feel that in my case, I would need to get rid of the current timer observable, and trigger a new one starting from the value we stopped at when pausing (and thus having to store that value). Any other operator could help me out here to not have to do that?
I create a timer with observables a while back. Perhaps it will give you some insight?
How does this work? The custom observable creates a stream that outputs the number on the stopwatch and is controlled by a separate stream (Here called control$
).
When control$
emits "START", the stopWatch starts, when it emits "STOP", the stopwatch stops, and when it emits "RESET" the stopwatch sets the counter back to zero. When control$
errors or completes, the stopwatch errors or completes.
function createStopwatch(control$: Observable<string>, interval = 1000): Observable<number>{
return defer(() => {
let toggle: boolean = false;
let count: number = 0;
const ticker = timer(0, interval).pipe(
map(x => count++)
);
const end$ = of("END");
return concat(
control$,
end$
).pipe(
catchError(_ => end$),
filter(control =>
control === "START" ||
control === "STOP" ||
control === "RESET" ||
control === "END"
),
switchMap(control => {
if(control === "START" && !toggle){
toggle = true;
return ticker;
}else if(control === "STOP" && toggle){
toggle = false;
return EMPTY;
}else if(control === "RESET"){
count = 0;
if(toggle){
return ticker;
}
}
return EMPTY;
})
);
});
}
If the control stream is going to be a subject, this is a good way to create the stopwatch.
function getStopWatch(interval: number = 1000): {
control$: Subject<string>,
display$: Observable<number>
} {
const control$ = new Subject<string>();
return {
control$,
display$: createStopwatch(control$, interval)
}
}
Stopwatch Object in Use:
const watch = getStopWatch();
watch.display$.subscribe(/*Numbers emitted here every interval once started by control$*/);
watch.control$.next("START");
watch.control$.next("STOP");
watch.control$.next("RESET");
// Completing the control cleans up everything
watch.control$.complete();
createStopwatch(merge(
fromEvent(startBtn, 'click').pipe(mapTo("START")),
fromEvent(stopBtn, 'click').pipe(mapTo("STOP"))
fromEvent(resetBtn, 'click').pipe(mapTo("RESET"))
)).subscribe(seconds => {
secondsField.innerHTML = seconds % 60;
minuitesField.innerHTML = Math.floor(seconds / 60) % 60;
hoursField.innerHTML = Math.floor(seconds / 3600);
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.