Wstrzymaj interwał rxjs

Nov 04 2020

Mam prosty stoper z wykorzystaniem rxjs

Problem polega na tym: nie można dostać, jak wstrzymać strumień moim przedziale, a następnie kontynuować ją stackbiz

Odpowiedzi

1 MrkSef Nov 04 2020 at 16:02

Widziałem pytania dotyczące stopera, które pojawiały się na tyle często, że pomyślałem, że interesujące byłoby utworzenie niestandardowego obserwowalnego stopera. RxJS sposób byłoby zastosowanie to przez włączenie do i / interwały timerów.

Innym interesującym sposobem implementacji tego jest użycie zamiast tego setTimeout. setTimeout powinno faktycznie wymagać nieco mniej pamięci, ponieważ nie polegamy na obserwowalnym aparacie, aby osiągnąć nasze cele czasowe

Jak to będzie działać? Nasze niestandardowe obserwowalne tworzy strumień, który wyświetla liczbę na stoperze i jest kontrolowany przez oddzielny strumień (nazywany tutaj control$). Tak więc, kiedy control$nada "START", stoper uruchamia się, gdy nada "STOP" stoper zatrzymuje się, a gdy nada "RESET" stoper ustawia licznik z powrotem na zero. Gdy control$wystąpi błąd lub zakończy się, stoper wyświetli błąd lub zakończy działanie.

Zaimplementowany za pomocą switchMap i Timer

function createStopwatch(control$: Observable<string>, interval = 1000): Observable<number>{
  return defer(() => {
    let toggle: boolean = false;
    let count: number = 0;

    const endTicker$ = new Subject(); const ticker = () => { return timer(0, interval).pipe( takeUntil(endTicker$),
        map(x => count++)
      )
    }
  
    return control$.pipe( tap({ next: _ => {/*Do nothing*/}, complete: () => { endTicker$.next();
          endTicker$.complete(); }, error: err => { endTicker$.next();
          endTicker$.complete();
        }
      }),
      filter(control => 
        control === "START" ||
        control === "STOP" ||
        control === "RESET"
      ),
      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;
      })
    );
  });
}

Zaimplementowane za pomocą setTimeout

function createStopwatch(control: Observable<string>, interval = 1000): Observable<number> {
  return new Observable(observer => {
    let count: number = 0;
    let tickerId: number = null;

    const clearTicker = () => {
      if(tickerId != null){
          clearTimeout(tickerId);
          tickerId = null;
        }
    }
    const setTicker = () => {
      const recursiveTicker = () => {
        tickerId = setTimeout(() => {
          observer.next(count++);
          recursiveTicker();
        }, interval);
      }
      clearTicker();
      observer.next(count++);
      recursiveTicker();
    }

    control.subscribe({
      next: input => {
        if(input === "START" && tickerId == null){
          setTicker();
        }else if(input === "STOP"){
          clearTicker();
        }else if(input === "RESET"){
          count = 0;
          if(tickerId != null){
            setTicker();
          }
        }
      },
      complete: () => {
        clearTicker();
        observer.complete();
      },
      error: err => {
        clearTicker();
        observer.error(err);
      }
    });
  
    return {unsubscribe: () => clearTicker()};
  });
}

StopWatch w użyciu

Oto przykład użycia tego obserwowalnego. Zarządzam strumieniem sterowania za pośrednictwem podmiotu, ale równie łatwo można go scalać / mapować zdarzenia DOM lub coś w tym rodzaju.

const control$ = new Subject<string>();
createStopwatch(control$, 250).subscribe(console.log); // We send a new action to our control stream every 1 second const actions = ["START", "STOP", "START", "RESET", "START"] zip(from(actions), interval(1000)).pipe( map((x,y) => x), finalize(() => { // After 5 seconds, unsubscribe via the control // If our control finishes in any way ( // completes, errors, or is unsubscribed), our // sopwatch reacts by doing the same. control$.complete();
  })
).subscribe(x => control$.next(x));

Stopwatch w użyciu # 2

To kontroluje stoper z setTimeoutzamiast interval.

const control$ = new Subject<string>();
createStopwatch(control$, 250).subscribe(console.log); // We send a new action to our control stream every 1 second const actions = ["START", "STOP", "START", "RESET", "START"] actions.forEach((val, index) => { setTimeout(() => { control$.next(val);
  },
  index * 1000);
})

// Unsubscribe via the control
setTimeout(() => {
  control$.complete();
}, actions.length * 1000);
1 JanRecker Nov 04 2020 at 12:36

Spójrz na moje rozwiązanie w tym stackblitzu

Komponent musi subskrybować usługę StopWatch. Nie podoba mi się pomysł nadania usłudze wartości, a następnie usługa ZMIENIA wartość niejawnie. Dlatego pracuję w sposób jawny, aby uzyskać zaktualizowany stoper (jako obserwowalny).

Pracuję z „timerem” zamiast „interwałem”, ponieważ „interwał” wyemitowałby pierwszą wartość (0) po sekundzie, dlatego mój stopWath miałby opóźnienie,

Jest mała sztuczka. Istnieje prywatna zmienna „timer $”, BehaviorSubject. A gdy tylko licznik zostanie uruchomiony, uruchamiam licznik czasu i subskrybuję go. W tej subskrypcji każda emisja licznika czasu wyemituje nową wartość dla BehaviorSubject.

Teraz przechowuję również subskrypcję.
Jeśli chcę zatrzymać wszystko, po prostu wypisuję się z „timera”. W rezultacie zegar zatrzymuje się. Ale ponieważ jest to trochę oddzielone od behawioru obiektu timer$, nadal będzie przechowywana ostatnia wartość.

Albo inaczej:
BehaviorSubject żyje wiecznie, konsument nigdy nie jest wypisany (tylko jeśli robi to sam). I w pewnym sensie „dołączam” i „odłączam” funkcję timera, gdy liczenie ma się rozpocząć lub zakończyć.

Nawiasem mówiąc, dobrym zwyczajem jest zawsze upewnianie się, że twój komponent anuluje subskrypcję wszystkich działających obserwowalnych, gdy zostanie zniszczony. Jeśli tak się nie stanie, te nadal aktywne subskrypcje mogą spowodować wydajność lub nawet gorsze problemy w aplikacji.

mam nadzieję, że to trochę pomoże

gorące pozdrowienia