使用递归 setTimeout 启动和停止计时器
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...
并且当用户按下停止键时,它应该停止这个定时器循环。如果我再次按下开始,改变按钮的状态(流变量),计时器应该再次做同样的事情(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);
}
我做错了什么?
计时器不会停止。即使我按下停止,它也会继续运行! (更改流状态值!)
您可以像这样定义自定义计时器:
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);
您可以更改在任何时刻执行的函数以及间隔。例如:运行 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,只需将其替换为新的即可。
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
的范围固定在您最初调用 setTimeout(repeatingFunc, 1000)
的时间点,这意味着状态和属性等内容可能包含过时的值。这可能会或可能不会成为问题,具体取决于您的上下文。
您可能还想添加一个 useEffect
清理函数,用于清除组件卸载时的超时。否则,即使组件不再安装,您的循环也会继续。
useEffect(() => {
if (!stream) {
ref.current = setTimeout(repeatingFunc, 1000);
}
return () => clearTimeout(ref.current);
}, [stream]);
假设 stream
是一个布尔值 (true
/false
) 以上将在 stream
的值更改或组件卸载时清除当前超时.
当stream
由true
变为false
时,会调用清理函数,清除超时。
当 stream
从 false
变为 true
时,清理函数也会被调用,但结果是 no-op 因为当前的 ref.current
不是注册时间更长。然后设置新的超时。
Passing an invalid ID to clearTimeout()
silently does nothing; no exception is thrown.
我正在尝试使用递归 setTimeout
启动和停止计时器,因为 setInterval
不符合我的需要。当用户按下开始时,计时器开始计时。
1...2...3...4..5...1...2...3..4...5...1..2... 3..4...5...
并且当用户按下停止键时,它应该停止这个定时器循环。如果我再次按下开始,改变按钮的状态(流变量),计时器应该再次做同样的事情(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);
}
我做错了什么?
计时器不会停止。即使我按下停止,它也会继续运行! (更改流状态值!)
您可以像这样定义自定义计时器:
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);
您可以更改在任何时刻执行的函数以及间隔。例如:运行 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,只需将其替换为新的即可。
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
的范围固定在您最初调用 setTimeout(repeatingFunc, 1000)
的时间点,这意味着状态和属性等内容可能包含过时的值。这可能会或可能不会成为问题,具体取决于您的上下文。
您可能还想添加一个 useEffect
清理函数,用于清除组件卸载时的超时。否则,即使组件不再安装,您的循环也会继续。
useEffect(() => {
if (!stream) {
ref.current = setTimeout(repeatingFunc, 1000);
}
return () => clearTimeout(ref.current);
}, [stream]);
假设 stream
是一个布尔值 (true
/false
) 以上将在 stream
的值更改或组件卸载时清除当前超时.
当stream
由true
变为false
时,会调用清理函数,清除超时。
当 stream
从 false
变为 true
时,清理函数也会被调用,但结果是 no-op 因为当前的 ref.current
不是注册时间更长。然后设置新的超时。
Passing an invalid ID to
clearTimeout()
silently does nothing; no exception is thrown.