如何使用RxJS观测值制作倒计时计时器?

如何使用RxJS观测值制作倒计时计时器?,rxjs,observable,reactive-programming,Rxjs,Observable,Reactive Programming,我正在努力使用可观测数据创建一个倒计时计时器,上的示例似乎不起作用。在此特定示例中,与timerInterval相关的错误不是计时器返回的可观测值的函数 我也在尝试其他方法,我想到的最好的方法是: Observable.interval(1000).take(10).subscribe(x => console.log(x)); 这里的问题是它从0数到10,我需要一个倒计时计时器,例如10,9,8…0 我也尝试过这个方法,但是对于类型Observable,计时器不存在 Observabl

我正在努力使用可观测数据创建一个倒计时计时器,上的示例似乎不起作用。在此特定示例中,与timerInterval相关的错误不是计时器返回的可观测值的函数

我也在尝试其他方法,我想到的最好的方法是:

Observable.interval(1000).take(10).subscribe(x => console.log(x));
这里的问题是它从0数到10,我需要一个倒计时计时器,例如10,9,8…0

我也尝试过这个方法,但是对于类型Observable,
计时器不存在

Observable.range(10, 0).timer(1000).subscribe(x => console.log(x));
以及,它根本不产生任何输出

Observable.range(10, 0).debounceTime(1000).subscribe(x => console.log(x));
为了澄清这一点,我需要关于ReactiveX的RxJS实现的帮助,而不是MircoSoft版本


您的思路是正确的-您的问题是,
计时器
不存在于原型上(因此在
Observable.range()
),而存在于Observable上(参见RxJS)。即


使用interval,可以指定每秒的时间长度

const time = 5 // 5 seconds
var timer$ = Rx.Observable.interval(1000) // 1000 = 1 second
timer$
  .take(time)
  .map((v)=>(time-1)-v) // to reach zero
  .subscribe((v)=>console.log('Countdown', v))
我是
take…()
情人,所以我使用
takeWhile()
如下(RxJS 6.x.x,以ES6的方式)


使用
timer
scan
takeWhile
如果不想依赖变量作为开始时间,则
scan
中的第三个参数是开始编号

timer$ = timer(0, 1000).pipe(
  scan(acc => --acc, 120),
  takeWhile(x => x >= 0)
);

我的计数器功能和显示时间:

import { Observable, timer, of, interval } from "rxjs";
import { map, takeWhile, take } from "rxjs/operators";

function countdown(minutes: number, delay: number = 0) {
   return new Observable<{ display: string; minutes: number; seconds: number }>(
      subscriber => {
        timer(delay, 1000)
          .pipe(take(minutes * 60))
          .pipe(map(v => minutes * 60 - 1 - v))
          .pipe(takeWhile(x => x >= 0))
          .subscribe(countdown => { // countdown => seconds
            const minutes = Math.floor(countdown / 60);
            const seconds = countdown - minutes * 60;

            subscriber.next({
              display: `${("0" + minutes.toString()).slice(-2)}:${("0" + seconds.toString()).slice(-2)}`,
              minutes,
              seconds
            });

            if (seconds <= 0 && minutes <= 0) {
              subscriber.complete();
            }
       });
   });
}

countdown(2).subscribe(next => {
  document.body.innerHTML = `<pre><code>${JSON.stringify(next, null, 4)}</code></pre>`;
});
`; });
输出,即:

// * startPoint => Value of timer continuing to go down
countDown(startPoint: number) {
  // * Fire Every Second
  const intervalObs = interval(1000);

  // * Shrink intervalObs subscription
  const disposeInterval = intervalObs.pipe(take(startPoint));

  // * Fire incremental number on every second
  disposeInterval.subscribe((second) => {
    this.countDownOnRetry = startPoint - second;
  });
}

这个例子对我很有用:)

顺便说一下,使用
takeWhile(val=>val>=0)
而不是
take(someNumber)
可能是有道理的,但它会检查-1,然后才完成。。晚了一秒钟

下面的示例将发射10、9、8、7、6、5、4、3、2、1、0。从10开始到0结束看起来很简单,但这对我来说相当棘手

const counter$=间隔(1000);//rxjs创建操作符-将每秒触发
常数numberOfSeconds=10;
计数器$.pipe(
扫描((累加器,U电流)=>累加器-1,秒数+1),
取(秒数+1),
//可选的
完成(()=>console.log(
'当发出0且可观察对象完成时,如果您愿意,可以执行某些操作'
))
)
这将起到同样的作用:


计数器$管道(
扫描((累加器,电流)=>累加器-1,秒数),
startWith(numberOfSeconds),//扫描不会第一次运行!
取(秒数+1),
//可选的
完成(()=>console.log(
'当发出0且可观察对象完成时,如果您愿意,可以执行某些操作'
))
)

当然,你可以做很多改变。。例如,您可以在
扫描之前
mapTo(-1)
,然后写入
累加器+电流
,电流
将为-1。

我还需要一个倒计时的间隔,因此我尝试了以下解决方案:


谢谢你的建议。它确实有效,只是感觉应该有更简单的方法来处理可观察对象。理想情况下,迭代器运算符允许倒计时,而不是只递增的范围(start,count)。希望其他人能提供一种方法。在此之前:您是否考虑过扩展Observable的原型以隐藏实现(例如)?有一个操作符可以做到这一点,
generate
它只是还没有被添加到新项目中。我在v4中看到了
generate
函数,我很惊讶它不在v5中。它将为我提供所需的功能。要点:请记住,任何计时器只要不断调用
timer(1000)
就会随时间推移而漂移。短时间内很好,但如果你正在编程一个时钟就不行了!如果需要精度,则需要使用系统时钟计算时间偏移。此计时器将延迟一秒结束,因为它检查-1,然后才完成。使用
take(120)
而不是
takeWhile()
将非常有效。
import { Observable, timer, of, interval } from "rxjs";
import { map, takeWhile, take } from "rxjs/operators";

function countdown(minutes: number, delay: number = 0) {
   return new Observable<{ display: string; minutes: number; seconds: number }>(
      subscriber => {
        timer(delay, 1000)
          .pipe(take(minutes * 60))
          .pipe(map(v => minutes * 60 - 1 - v))
          .pipe(takeWhile(x => x >= 0))
          .subscribe(countdown => { // countdown => seconds
            const minutes = Math.floor(countdown / 60);
            const seconds = countdown - minutes * 60;

            subscriber.next({
              display: `${("0" + minutes.toString()).slice(-2)}:${("0" + seconds.toString()).slice(-2)}`,
              minutes,
              seconds
            });

            if (seconds <= 0 && minutes <= 0) {
              subscriber.complete();
            }
       });
   });
}

countdown(2).subscribe(next => {
  document.body.innerHTML = `<pre><code>${JSON.stringify(next, null, 4)}</code></pre>`;
});
{
   "display": "01:56",
   "minutes": 1,
   "seconds": 56
}
// * startPoint => Value of timer continuing to go down
countDown(startPoint: number) {
  // * Fire Every Second
  const intervalObs = interval(1000);

  // * Shrink intervalObs subscription
  const disposeInterval = intervalObs.pipe(take(startPoint));

  // * Fire incremental number on every second
  disposeInterval.subscribe((second) => {
    this.countDownOnRetry = startPoint - second;
  });
}