为什么我的 RxJS 倒计时时钟不显示?

2024-02-24

我正在开发一个 Angular 9 测验应用程序,我正在使用 RxJS 作为倒计时器(在container\scoreboard\time\time.component.ts 中),并且计时器似乎没有显示。 stopTimer() 函数应在计时器停止的秒数处停止计时器。选择正确答案后,计时器应停止,并且计时器应在问题之间重置。每个问题所花费的时间应保存到 elapsedTimes 数组中。请参阅我在 Stackblitz 上的 TimeComponent 中的计时器代码:https://stackblitz.com/edit/angular-9-quiz-app https://stackblitz.com/edit/angular-9-quiz-app。谢谢。

下面的代码是我第一次使用 RxJS 构建倒计时时钟。我最新的代码位于 Stackblitz 上。

  countdownClock() {
    this.timer = interval(1000)
      .pipe(
        takeUntil(this.isPause),
        takeUntil(this.isStop)
      );
    this.timerObserver = {
      next: (_: number) => {
        this.timePerQuestion -= 1;
          ...
        }
    };

    this.timer.subscribe(this.timerObserver);
  }

  goOn() {
    this.timer.subscribe(this.timerObserver);
  }

  pauseTimer() {
    this.isPause.next();
    // setTimeout(() => this.goOn(), 1000)
  }

  stopTimer() {
    this.timePerQuestion = 0;
    this.isStop.next();
  }

我在 TimerService 中使用 stopTimer() 和pauseTimer(),这样我就可以从不同的组件调用它们。


与每个复杂的问题一样,您必须将其分解为更小的、易于理解的问题。

因此,我创建了一个堆栈闪电战 https://stackblitz.com/edit/typescript-pjytrb?file=index.ts重新创建基本功能的演示:

  • 启动计时器
  • 暂时停止(标记时间戳);例如,当选择所有答案时
  • 从最后一个时间戳继续
  • 重置定时器
  • 停止计时器

这是代码:

const $ = document.querySelector.bind(document);

const start$ = fromEvent($('#start'), 'click').pipe(shareReplay(1));
const reset$ = fromEvent($('#reset'), 'click');
const stop$ = fromEvent($('#stop'), 'click');
const markTimestamp$ = fromEvent($('#mark'), 'click');
const continueFromLastTimestamp$ = fromEvent($('#continue'), 'click');

const src$ = concat(
  start$.pipe(first()),
  reset$
).pipe(
  switchMapTo(
    timer(0, 1000)
      .pipe(
        takeUntil(markTimestamp$),
        repeatWhen(
          completeSbj => completeSbj.pipe(switchMapTo(
            continueFromLastTimestamp$.pipe(first())
          ))
        ),
        scan((acc, crt) => acc + 1000, 0)
      )
  ),
  takeUntil(stop$),
  repeatWhen(completeSbj => completeSbj.pipe(switchMapTo(start$.pipe(skip(1), first()))))
).subscribe(console.log)

让我们逐一了解每个相关部分。


concat(
  start$.pipe(first()),
  reset$
).pipe(switchMapTo(timer(...)))

The timer仅当之前未开始时才会从 0 开始计数(start$.pipe(first()))或者用户想要重置所有内容(reset$).

concat(a$, b$)确保b$不能发射,除非a$完成。


timer(0, 1000)
  .pipe(
    takeUntil(markTimestamp$),
    repeatWhen(
      completeSbj => completeSbj.pipe(switchMapTo(
        continueFromLastTimestamp$.pipe(first())
      ))
    ),
    scan((acc, crt) => acc + 1000, 0)
  )

我们希望计时器一直处于活动状态,直到markTimestamp$发出。当这种情况发生时,源(timer(0, 1000)) 将被取消订阅。和repeatWhen我们可以决定什么时候应该timer重新订阅。也就是说,当continueFromLastTimestamp$.pipe(first())发出。重要的是我们使用first(),否则源可能会被重新订阅多次。

Placing scan((acc, crt) => acc + 1000, 0) after repeatWhen确保最后的时间戳不会丢失。例如,计时器可能开始于X, at X+5用户触发markTimestamp$然后在X + 100用户触发continueFromLastTimestamp$。发生这种情况时,计时器将发出X+6.


takeUntil(stop$),
repeatWhen(completeSbj => completeSbj.pipe(switchMapTo(start$.pipe(skip(1), first()))))

计时器应处于活动状态,直到stop$发出。然后,只有用户触发才能重新启动start$ again. skip(1)使用是因为我们不使用缓存值ReplaySubject被使用过start$'s shareReplay and first()使用是因为source应该重新订阅 只有一次.


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么我的 RxJS 倒计时时钟不显示? 的相关文章

随机推荐