以rxjs间隔重新启动计时器

时间:2019-04-16 21:13:22

标签: javascript angular typescript rxjs

我创建了一个类,该类设置了可观察到的可暂停的rxjs:

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;
  }
}

这似乎工作正常,但是我要解决的问题是我希望计时器在不暂停时重置。因此,假设间隔时间是上一次发出间隔setPaused(false)被调用之后的10秒和5秒。在这种情况下,我希望它立即发出,然后重新启动计时器。这样添加起来会很容易吗?

5 个答案:

答案 0 :(得分:3)

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.

答案 1 :(得分:1)

您可以通过以下代码段实现您描述的行为:

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观察对象发出true时,switchMap运算符将返回一个新的interval观察对象。
  • 在取消暂停时,使用startWith运算符立即发出事件。
  • 如果希望在订阅可观察对象时使间隔自动开始,则只需使用true初始化BehaviorSubject。

StackBlitz Example

答案 2 :(得分:0)

You can abandon the old timer on start and start a new one on start.

const { interval, Subject, fromEvent } = rxjs;
const { takeUntil } = rxjs.operators;

let timer$;

const pause = new Subject();

const obs$ = new Subject();

obs$.subscribe(_ => { console.log('Timer fired') });

function start() {
  timer$ = interval(1000);
  timer$.pipe(takeUntil(pause)).subscribe(_ => { obs$.next(); });
}

function stop() {
  pause.next();
  timer$ = undefined;
}

fromEvent(document.getElementById('toggle'), 'click').subscribe(() => {
  if (timer$) {
    stop();
  } else {
    start();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
<button id="toggle">Start/Stop</button>

答案 3 :(得分:0)

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/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>

<button id="start">start</button>
<button id="stop">stop</button>

And a simpler one, that merges 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/rxjs@6.4.0/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/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>

<button id="start">start</button>
<button id="stop">stop</button>

Just wanted to join this party :)

答案 4 :(得分:0)

检查此代码

/**
* it is a simple timer created by via rxjs
* @author KentWood
* email minzojian@hotmail.com
*/
function rxjs_timer(interval, times, tickerCallback, doneCallback, startDelay) {
    this.pause = function () {
        this.paused = true;
    }
    this.resume = function () {
        this.paused = false;
    }
    this.stop = function () {
        if (this.obs) {
            this.obs.complete();
            this.obs.unsubscribe();
        }
        this.obs = null;

    }
    this.start = function (interval, times, tickerCallback, doneCallback, startDelay) {
        this.startDelay = startDelay || 0;
        this.interval = interval || 1000;
        this.times = times || Number.MAX_VALUE;
        this.currentTime = 0;
        this.stop();

        rxjs.Observable.create((obs) => {
            this.obs = obs;
            let p = rxjs.timer(this.startDelay, this.interval).pipe(
                rxjs.operators.filter(() => (!this.paused)), 
              rxjs.operators.tap(() => {
                    if (this.currentTime++ >= this.times) {
                        this.stop();
                    }
                }),
              rxjs.operators.map(()=>(this.currentTime-1))
            );
            let sub = p.subscribe(val => obs.next(val), err => obs.error(err), () => obs
                .complete());
            return sub;
        }).subscribe(tickerCallback, null, doneCallback);
    }
    this.start(interval, times, tickerCallback, doneCallback, startDelay);
}
/////////////test/////////////
var mytimer = new rxjs_timer(
1000/*interval*/, 
10 /*times*/, 
(v) => {logout(`time:${v}`)}/*tick callback*/, 
() => {logout('done')}/*complete callback*/, 
2000/*start delay*/);

//call mytimer.pause() 
//call mytimer.resume() 
//call mytimer.stop() 




function logout(str){
  document.getElementById('log').insertAdjacentHTML( 'afterbegin',`<p>${str}</p>`)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.1/rxjs.umd.js"></script>

<button onclick="mytimer.pause()"> pause</button>
<button onclick="mytimer.resume()"> resume</button>
<button onclick="mytimer.stop()"> stop</button>
<div id='log'></div>