如何使用 RxJS Observables 制作倒数计时器?
How to make countdown timer with RxJS Observables?
我正在努力使用 Observables 创建倒数计时器,http://reactivex.io/documentation/operators/timer.html 中的示例似乎不起作用。在这个具体的例子中,与 timerInterval 相关的错误不是从定时器返回的 Observable 的函数。
我也一直在尝试其他方法,我想出的最好方法是:
Observable.interval(1000).take(10).subscribe(x => console.log(x));
这里的问题是它从 0 计数到 10,我想要一个倒数计时器,例如10,9,8...0.
我也试过了,但是 timer
类型不存在 Observable
Observable.range(10, 0).timer(1000).subscribe(x => console.log(x));
以及,它根本不产生任何输出。
Observable.range(10, 0).debounceTime(1000).subscribe(x => console.log(x));
To clarify I need help with ReactiveX's RxJS implementation, not the MircoSoft version.
你走在正确的轨道上 - 你的问题是 timer
在原型上不存在(因此在 Observable.range()
上)但在 Observable 上(参见 RxJS docs). I.e. jsbin
const start = 10;
Rx.Observable
.timer(100, 100) // timer(firstValueDelay, intervalBetweenValues)
.map(i => start - i)
.take(start + 1)
.subscribe(i => console.log(i));
// or
Rx.Observable
.range(0, start + 1)
.map(i => start - i)
.subscribe(i => console.log(i));
带间隔,允许您指定一秒有多长
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 方式)
import {timer} from 'rxjs';
import {takeWhile, tap} from 'rxjs/operators';
let counter = 10;
timer(1000, 1000) //Initial delay 1 seconds and interval countdown also 1 second
.pipe(
takeWhile( () => counter > 0 ),
tap(() => counter--)
)
.subscribe( () => {
console.log(counter);
} );
使用 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>`;
});
输出即:
{
"display": "01:56",
"minutes": 1,
"seconds": 56
}
这个例子对我有用:)
顺便说一下,使用 takeWhile(val => val >= 0)
而不是 take(someNumber)
可能有意义,但它会检查 -1 然后才完成.. 1 秒太晚了。
下面的示例将发出 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0。从 10 开始到 0 结束似乎微不足道,但对我来说却相当棘手。
const counter$ = interval(1000); // rxjs creation operator - will fire every second
const numberOfSeconds = 10;
counter$.pipe(
scan((accumulator, _current) => accumulator - 1, numberOfSeconds + 1),
take(numberOfSeconds + 1),
// optional
finalize(() => console.log(
'something to do, if you want to, when 0 is emitted and the observable completes'
))
)
这将做同样的事情:
counter$.pipe(
scan((accumulator, current) => accumulator - 1, numberOfSeconds),
startWith(numberOfSeconds), // scan will not run the first time!
take(numberOfSeconds + 1),
// optional
finalize(() => console.log(
'something to do, if you want to, when 0 is emitted and the observable completes'
))
)
你当然可以做很多改变..你可以在 scan
之前 mapTo(-1)
例如,然后写 accumulator + current
然后 current
将是-1.
我还需要一个向后计数的间隔,所以我尝试了这个解决方案:
const { interval, take } = rxjs
const countDownEl = document.querySelector("#countdown");
/**
* Coundown with RxJs
* @param startPoint {number} Value of timer continuing to go down
*/
function countDown(startPoint) {
// * 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) => {
console.log("Second: ", second);
countDownEl.innerHTML = startPoint - second;
})
}
countDown(10);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.3.0/rxjs.umd.min.js"></script>
<label>Countdown: <span id="countdown"></span></label>
恕我直言,这是最简单的方法:
import { interval } from 'rxjs'
import { map, take } from 'rxjs/operators'
const durationInSeconds = 1 * 60 // 1 minute
interval(1000).pipe(take(durationInSeconds), map(count => durationInSeconds - count)).subscribe(countdown => {
const hrs = (~~(countdown / 3600)).toString()
const mins = (~~((countdown % 3600) / 60)).toString()
const secs = (~~countdown % 60).toString()
console.log(`${hrs.padStart(2, '0')}:${mins.padStart(2, '0')}:${secs.padStart(2, '0')}`);
})
或者这样:)
interval(1000)
.take(10)
.map((t) => Math.abs(t - 10))
.subscribe((t) => {
console.log(t);
});
我正在努力使用 Observables 创建倒数计时器,http://reactivex.io/documentation/operators/timer.html 中的示例似乎不起作用。在这个具体的例子中,与 timerInterval 相关的错误不是从定时器返回的 Observable 的函数。
我也一直在尝试其他方法,我想出的最好方法是:
Observable.interval(1000).take(10).subscribe(x => console.log(x));
这里的问题是它从 0 计数到 10,我想要一个倒数计时器,例如10,9,8...0.
我也试过了,但是 timer
类型不存在 Observable
Observable.range(10, 0).timer(1000).subscribe(x => console.log(x));
以及,它根本不产生任何输出。
Observable.range(10, 0).debounceTime(1000).subscribe(x => console.log(x));
To clarify I need help with ReactiveX's RxJS implementation, not the MircoSoft version.
你走在正确的轨道上 - 你的问题是 timer
在原型上不存在(因此在 Observable.range()
上)但在 Observable 上(参见 RxJS docs). I.e. jsbin
const start = 10;
Rx.Observable
.timer(100, 100) // timer(firstValueDelay, intervalBetweenValues)
.map(i => start - i)
.take(start + 1)
.subscribe(i => console.log(i));
// or
Rx.Observable
.range(0, start + 1)
.map(i => start - i)
.subscribe(i => console.log(i));
带间隔,允许您指定一秒有多长
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 方式)
import {timer} from 'rxjs';
import {takeWhile, tap} from 'rxjs/operators';
let counter = 10;
timer(1000, 1000) //Initial delay 1 seconds and interval countdown also 1 second
.pipe(
takeWhile( () => counter > 0 ),
tap(() => counter--)
)
.subscribe( () => {
console.log(counter);
} );
使用 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>`;
});
输出即:
{
"display": "01:56",
"minutes": 1,
"seconds": 56
}
这个例子对我有用:)
顺便说一下,使用 takeWhile(val => val >= 0)
而不是 take(someNumber)
可能有意义,但它会检查 -1 然后才完成.. 1 秒太晚了。
下面的示例将发出 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0。从 10 开始到 0 结束似乎微不足道,但对我来说却相当棘手。
const counter$ = interval(1000); // rxjs creation operator - will fire every second
const numberOfSeconds = 10;
counter$.pipe(
scan((accumulator, _current) => accumulator - 1, numberOfSeconds + 1),
take(numberOfSeconds + 1),
// optional
finalize(() => console.log(
'something to do, if you want to, when 0 is emitted and the observable completes'
))
)
这将做同样的事情:
counter$.pipe(
scan((accumulator, current) => accumulator - 1, numberOfSeconds),
startWith(numberOfSeconds), // scan will not run the first time!
take(numberOfSeconds + 1),
// optional
finalize(() => console.log(
'something to do, if you want to, when 0 is emitted and the observable completes'
))
)
你当然可以做很多改变..你可以在 scan
之前 mapTo(-1)
例如,然后写 accumulator + current
然后 current
将是-1.
我还需要一个向后计数的间隔,所以我尝试了这个解决方案:
const { interval, take } = rxjs
const countDownEl = document.querySelector("#countdown");
/**
* Coundown with RxJs
* @param startPoint {number} Value of timer continuing to go down
*/
function countDown(startPoint) {
// * 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) => {
console.log("Second: ", second);
countDownEl.innerHTML = startPoint - second;
})
}
countDown(10);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.3.0/rxjs.umd.min.js"></script>
<label>Countdown: <span id="countdown"></span></label>
恕我直言,这是最简单的方法:
import { interval } from 'rxjs'
import { map, take } from 'rxjs/operators'
const durationInSeconds = 1 * 60 // 1 minute
interval(1000).pipe(take(durationInSeconds), map(count => durationInSeconds - count)).subscribe(countdown => {
const hrs = (~~(countdown / 3600)).toString()
const mins = (~~((countdown % 3600) / 60)).toString()
const secs = (~~countdown % 60).toString()
console.log(`${hrs.padStart(2, '0')}:${mins.padStart(2, '0')}:${secs.padStart(2, '0')}`);
})
或者这样:)
interval(1000)
.take(10)
.map((t) => Math.abs(t - 10))
.subscribe((t) => {
console.log(t);
});