使用 clearTimeout() 后递归 setTimeout() 循环没有停止
Recursive setTimeout() loop not stopped after using clearTimeout()
我正在制作一个简单的倒数计时器,显示剩余的秒数和秒数。当用户单击按钮时,倒计时应该停止,并且应该显示一些文本而不是计时器。在 PC 上一切正常,但如果我将我的 phone 与更糟糕的 cpu 一起使用,我有时会出现这种意外行为:当用户单击按钮时,文本显示几分之一秒,然后计时器保持继续。我认为这是由函数 运行 时调用 clearTimeout() 方法引起的,就在调用新超时之前,但 post 证明我错了。您知道是什么导致了这种行为吗?我应该如何解决它?
const finished = () => { // finished() is also called when user clicks a button
// show some thext on the screen
clearTimeout(timeout);
}
const start = document.timeline.currentTime! // Think of it as Date.now(); also returns a ms value; just slightly faster.
const end = start + 30000 // the countodwn takes 30 seconds
let timeout:any;
const frame = () => {
const elapsed = Math.floor(end - document.timeline.currentTime!)
if (elapsed <= 0) {
finished()
return
};
let secs: number | string = Math.floor(elapsed / 1000)
let setsecs: number | string = Math.floor((elapsed % 1000) * 0.1)
secs = secs < 10 ? "0" + secs : secs
setsecs = setsecs < 10 ? "0" + setsecs : setsecs
result.innerText = `Time left: ${secs}.${setsecs}`
timeout = setTimeout(() => requestAnimationFrame(frame), 15 - elapsed % 15) // raf is for optimisation and I'm substracting the timeout by the time that has already passed for drift minimisation
}
frame()
编辑:我设法在我的 phone 上重新创建了缓慢的行为,并承诺:Here's the code
TL;DR
问题
setTimeout
和 clearTimeout
之间的时间太短了。在 clearTimeout
.
之后触发重新安排
例子
使用布尔标志检查是否调用了 finished
函数。
解决方案
const start = document.timeline.currentTime! // Think of it as Date.now(); also returns a ms value; just slightly faster.
const end = start + 3000 // the countodwn takes 3 seconds
let timeout:any;
let finishedCalled = false
const finished = () => { // finished() is also called when user clicks a button
// show some thext on the screen
finishedCalled = true
clearTimeout(timeout)
console.log(`Finished!`)
}
setTimeout(finished, 2000)
const frame = async () => {
if(finishedCalled) return console.log(`Finished called found. Have to be end here.`)
const elapsed = Math.floor(end - document.timeline.currentTime!)
console.log(`Elapsed:`, elapsed)
if (elapsed <= 0) {
finished()
return
};
let secs: number | string = Math.floor(elapsed / 1000)
let setsecs: number | string = Math.floor((elapsed % 1000) * 0.1)
secs = secs < 10 ? "0" + secs : secs
setsecs = setsecs < 10 ? "0" + setsecs : setsecs
console.log(`Time left: ${secs}.${setsecs}`)
await new Promise((resolve) => setTimeout(resolve, 500));
timeout = setTimeout(() => {
console.log(`Timer fired!`)
requestAnimationFrame(frame)
}, 10) // raf is for optimisation and I'm substracting the timeout by the time that has already passed for drift minimisation
}
frame()
深入挖掘
以Node.js为例,追根溯源
clearTimeout
called unenroll
unenroll
called emitDestroy
emitDestroy
=== emitDestroyScript
emitDestroyScript
called async_wrap.queueDestroyAsyncId
async_wrap.queueDestroyAsyncId
called AsyncWrap::EmitDestroy
AsyncWrap::EmitDestroy
可能会或可能不会立即执行。
到此为止,我们可以知道这是一个异步调用,可以在执行之前重新安排。这就是 finished
函数无法停止 timer
.
的原因
Disclaimer: I am not an expert of C++. Please feel free to improve the answer.
我正在制作一个简单的倒数计时器,显示剩余的秒数和秒数。当用户单击按钮时,倒计时应该停止,并且应该显示一些文本而不是计时器。在 PC 上一切正常,但如果我将我的 phone 与更糟糕的 cpu 一起使用,我有时会出现这种意外行为:当用户单击按钮时,文本显示几分之一秒,然后计时器保持继续。我认为这是由函数 运行 时调用 clearTimeout() 方法引起的,就在调用新超时之前,但
const finished = () => { // finished() is also called when user clicks a button
// show some thext on the screen
clearTimeout(timeout);
}
const start = document.timeline.currentTime! // Think of it as Date.now(); also returns a ms value; just slightly faster.
const end = start + 30000 // the countodwn takes 30 seconds
let timeout:any;
const frame = () => {
const elapsed = Math.floor(end - document.timeline.currentTime!)
if (elapsed <= 0) {
finished()
return
};
let secs: number | string = Math.floor(elapsed / 1000)
let setsecs: number | string = Math.floor((elapsed % 1000) * 0.1)
secs = secs < 10 ? "0" + secs : secs
setsecs = setsecs < 10 ? "0" + setsecs : setsecs
result.innerText = `Time left: ${secs}.${setsecs}`
timeout = setTimeout(() => requestAnimationFrame(frame), 15 - elapsed % 15) // raf is for optimisation and I'm substracting the timeout by the time that has already passed for drift minimisation
}
frame()
编辑:我设法在我的 phone 上重新创建了缓慢的行为,并承诺:Here's the code
TL;DR
问题
setTimeout
和 clearTimeout
之间的时间太短了。在 clearTimeout
.
例子
使用布尔标志检查是否调用了 finished
函数。
解决方案
const start = document.timeline.currentTime! // Think of it as Date.now(); also returns a ms value; just slightly faster.
const end = start + 3000 // the countodwn takes 3 seconds
let timeout:any;
let finishedCalled = false
const finished = () => { // finished() is also called when user clicks a button
// show some thext on the screen
finishedCalled = true
clearTimeout(timeout)
console.log(`Finished!`)
}
setTimeout(finished, 2000)
const frame = async () => {
if(finishedCalled) return console.log(`Finished called found. Have to be end here.`)
const elapsed = Math.floor(end - document.timeline.currentTime!)
console.log(`Elapsed:`, elapsed)
if (elapsed <= 0) {
finished()
return
};
let secs: number | string = Math.floor(elapsed / 1000)
let setsecs: number | string = Math.floor((elapsed % 1000) * 0.1)
secs = secs < 10 ? "0" + secs : secs
setsecs = setsecs < 10 ? "0" + setsecs : setsecs
console.log(`Time left: ${secs}.${setsecs}`)
await new Promise((resolve) => setTimeout(resolve, 500));
timeout = setTimeout(() => {
console.log(`Timer fired!`)
requestAnimationFrame(frame)
}, 10) // raf is for optimisation and I'm substracting the timeout by the time that has already passed for drift minimisation
}
frame()
深入挖掘
以Node.js为例,追根溯源
clearTimeout
calledunenroll
unenroll
calledemitDestroy
emitDestroy
===emitDestroyScript
emitDestroyScript
calledasync_wrap.queueDestroyAsyncId
async_wrap.queueDestroyAsyncId
calledAsyncWrap::EmitDestroy
AsyncWrap::EmitDestroy
可能会或可能不会立即执行。
到此为止,我们可以知道这是一个异步调用,可以在执行之前重新安排。这就是 finished
函数无法停止 timer
.
Disclaimer: I am not an expert of C++. Please feel free to improve the answer.