[英]Start and Stop a Timer with Recursive setTimeout
我正在尝试使用递归setTimeout
启动和停止计时器,因为setInterval
不符合我的需要。 当用户按下开始时,计时器开始计时。
1...2...3...4..5...1...2...3..4...5...1..2...3..4。 ..5...
当用户按下停止时,它应该停止这个定时器循环。 如果我再次按下开始,更改按钮(流变量)的 state,计时器应该再次做同样的事情(1..2..3..4..5..1..2...3。 .4...5..)
我认为这样做的方法就是这样。
const ref = useRef(null);
useEffect(() => {
if (!stream) {
ref.current=setTimeout(repeatingFunc, 1000);
} else {
clearTimeout(ref.current);
}
}, [stream]);
function repeatingFunc() {
console.log("It's been 5 seconds. Execute the function again.");
setTimeout(repeatingFunc, 5000);
}
我究竟做错了什么?
计时器不会停止。 即使我按下停止,它也会继续运行! (更改 stream state 值!)
您可以像这样定义自定义计时器:
function CustomTimer(func) {
this.id = -1;
this.func = func;
this.start = function (interval) {
this.stop();
if (this.func) {
var t = this;
this.id = setTimeout(function () { t.func.call(t) }, interval || 1000);
}
}
this.stop = function () {
if (this.id >= 0) {
clearTimeout(this.id);
this.id = -1;
}
}
}
并使用它:
var myTimer = new CustomTimer(function () {
console.log("It's been 5 seconds. Execute the function again.");
this.start(5000);
});
myTimer.start(5000);
您可以将 function 更改为随时执行,也可以更改间隔。 例如:在 1 秒内运行启动事件,然后每 5 秒运行 5 次其他操作:
var secondFunc = function () {
console.log("It's been 5 seconds. Execute the function again.");
if (++this.count < 5)
this.start(5000);
}
var myTimer = new CustomTimer(function () {
console.log("First event at 1 second");
this.count = 0;
this.func = secondFunc;
this.start(5000);
});
myTimer.start(1000);
计时器不停止的原因是因为您只存储了初始setTimeout
调用的超时 ID。 当repeatingFunc
被调用时,另一个setTimeout
回调被注册,它有一个新的超时 ID。
因此,当您尝试使用clearTimeout(ref.current)
清除超时时,您传递的是一个过时的超时 ID。
由于之前的setTimeout
注册已经被调用,你不再需要存储之前的 ID,你可以简单地用新的 ID 替换它。
const ref = useRef(null);
useEffect(() => {
if (!stream) {
ref.current = setTimeout(repeatingFunc, 1000);
} else {
clearTimeout(ref.current);
}
}, [stream]);
function repeatingFunc() {
console.log("It's been 5 seconds. Execute the function again.");
ref.current = setTimeout(repeatingFunc, 5000);
// ^^^^^^^^^^^^ update timeout ID after registering a new timeout
}
请注意, repeatingFunc
的 scope 固定在您最初调用setTimeout(repeatingFunc, 1000)
的时间点,这意味着状态和属性等内容可能包含过时的值。 这可能会或可能不会成为问题,具体取决于您的上下文。
您可能还想添加一个useEffect
cleanup function 来清除组件卸载的超时。 否则,即使不再安装组件,您的循环也会继续。
useEffect(() => {
if (!stream) {
ref.current = setTimeout(repeatingFunc, 1000);
}
return () => clearTimeout(ref.current);
}, [stream]);
假设stream
是 boolean ( true
/ false
),只要stream
的值发生变化,或者在组件卸载时,上述内容就会清除当前超时。
当stream
从true
变为false
时,将调用清理 function 并清除超时。
当stream
从false
变为true
时,清理 function 也会被调用,但会导致无操作,因为当前的ref.current
不再被注册。 然后设置新的超时。
将无效 ID 传递给
clearTimeout()
静默不执行任何操作; 没有异常被抛出。
请参阅: MDN - clearTimeout()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.