我正在开发一个 Angular 9 测验应用程序,我正在使用 RxJS 作为倒数计时器(在容器\记分板\时间\time.component.ts 中),但计时器似乎没有显示。stopTimer() 函数应该在计时器停止的秒数上停止计时器。选择正确答案后计时器应停止,并且计时器应在问题之间重置。每个问题经过的时间应保存到 elapsedTimes 数组中。请在 Stackblitz 上的 TimeComponent 中查看我的计时器代码: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();
}
Run Code Online (Sandbox Code Playgroud)
我在 TimerService 中使用了 stopTimer() 和 pauseTimer(),因此我可以从不同的组件调用它们。
与每个复杂的问题一样,您必须将其分解为更小的、可消化的问题。
因此,我创建了一个重新创建基本功能的StackBlitz演示:
这是代码:
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)
Run Code Online (Sandbox Code Playgroud)
让我们来看看每个相关部分。
concat(
start$.pipe(first()),
reset$
).pipe(switchMapTo(timer(...)))
Run Code Online (Sandbox Code Playgroud)
该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)
)
Run Code Online (Sandbox Code Playgroud)
我们希望计时器一直处于活动状态,直到markTimestamp$发出。当这种情况发生时,source( timer(0, 1000)) 将被取消订阅。随着repeatWhen我们可以决定什么时候应该timer进行重新订阅。也就是说,当continueFromLastTimestamp$.pipe(first())发射时。我们使用 很重要first(),否则源可能会被多次重新订阅。
配售scan((acc, crt) => acc + 1000, 0)后,repeatWhen确保了最后的时间戳不会丢失。例如,计时器可能从 开始X,在X+5用户触发时markTimestamp$,然后在X + 100用户触发时continueFromLastTimestamp$。发生这种情况时,计时器将发出X+6.
takeUntil(stop$),
repeatWhen(completeSbj => completeSbj.pipe(switchMapTo(start$.pipe(skip(1), first()))))
Run Code Online (Sandbox Code Playgroud)
计时器应该处于活动状态,直到stop$发出。然后,只有用户start$再次触发它才能重新启动。skip(1)用来因为我们不被缓存值ReplaySubject使用start$的shareReplay和first()被使用,因为源应重新订阅 一次。
| 归档时间: |
|
| 查看次数: |
543 次 |
| 最近记录: |