简体   繁体   中英

Task to do StopWatch on RxJs using Observables

I have a task to do StopWatch. I did it using Subject, but I should do it via Observables. I read about at rxjs.dev. But I can`t understand it. Help me plz

The stopwatch must have the following buttons:

"Start / Stop" - start / stop timing, stops and resets the stopwatch value.

"Wait" - works on a double click (the time between clicks is not more than 300 ms;) The stopwatch should stop timing, if you press start after it. then the countdown is resumed.

"Reset" - reset the stopwatch to 0. Resets the stopwatch and starts counting again.

import "./App.css";
import { useEffect, useState } from "react";
import { interval, Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
 


function App() {
  const [time, setTime] = useState(0);
  const [status, setStatus] = useState(false);
 
  useEffect(() => {
    const unsubscribe$ = new Subject();
    interval(10)
      .pipe(takeUntil(unsubscribe$))
      .subscribe(() => {
        if (status === true) {
          setTime(val => val + 10);
        }
      });
    return () => {
      unsubscribe$.next();
      unsubscribe$.complete();
    };
  }, [status]);
 
   const start = React.useCallback(() => {
    setStatus(true);
  }, []);
 
  const stop = React.useCallback(() => {
    setStatus(false);
  }, []);
 
  const reset = React.useCallback(() => {
    setTime(0);
   setStatus(false);
  }, []);
  console.log(time);
  return (
    <div className="App">
    <h1>Секундомер</h1>
    <span>{Math.floor(time / (1000 * 60 * 60)) % 24}</span>
    <span>{Math.floor((time / (1000 * 60)) % 60)}</span>
    <span>{Math.floor((time / 1000) % 60)}</span>
    <div>
      <button onClick={()=> start()} class="start">Старт</button>
      <button onClick={()=> stop()}class="stop">Стоп</button>
      <button onClick={()=> reset()} class="reset">Перезапуск</button>
    </div>
    </div>
  );
}
export default App;```

I created a pretty generic stopwatch implementation a while back.

type StopwatchAction = "START" | "STOP" | "RESET" | "END";

function createStopwatch(
  control$: Observable<StopwatchAction>, 
  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$),
      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;
      })
    );
  });
}

Using this is just a matter of hooking the correct events into a stream of StopwatchAction s. This example only sends "START" and "RESET" based on button clicks, but you can merge in "STOP" and "END" as well.

For example:

createStopwatch(merge(
  fromEvent(startBtn, 'click').pipe(mapTo("START")),
  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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM