如何解决嵌套在设置间隔中的超时设置中的计数器在每个回调中多次求和
我有一些代码旨在更新设置间隔块内的计数器。我的意图是每次调用设置的超时函数时都会更新一次计数器。但似乎 setTimeout 的回调在超时的整个持续时间内都处于活动状态,因此计数器在 3 秒超时时间内重复求和,导致它的结束值为 8 而不是所需的值 2。 我试图了解如何实现这一点,在超时后立即调用计数器,并且只调用一次。不幸的是,简单地把它放在函数之后似乎并不能解决这个问题。
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
setTimeout(()=>{
flag = 0;
count++;
},3000);
}
},500);
解决方法
那个?
let count = 1,flag = 1,noTimCall = true
;
setInterval(() =>
{
if (flag === 1 && noTimCall)
{
noTimCall = false
setTimeout(()=>
{
flag = 0
count++
noTimCall = true
},3000)
}
},500)
,
让我们一步一步地检查您的代码。
- 0 毫秒自开始。
flag
是1
⇒ 创建的第一个超时。 - 自开始以来 500 毫秒。
flag
是1
⇒ ... - …
- 自开始以来 3000 毫秒。
flag
是1
⇒ 创建的第 7 次超时。 - 执行第一个超时:将
flag
设置为0
,将count
增加为2
。 - 自开始以来 3500 毫秒。
flag
是0
⇒ 实际上什么都没有。 - 已执行第二次超时:……,将
count
增加到3
。 - 自开始以来 4000 毫秒。
flag
是0
⇒ ... - 第三次超时执行:……
- …
- 自开始以来 6000 毫秒。 ……
- 已执行第 7 次超时:……,将
count
增加到8
。
如您所见,将创建 7 个超时,每个超时以 count
递增 1,在 count
处以 8
结尾。
有多种方法可以在延迟后仅增加一次 count
:
- 不使用超时。我们可以手动跟踪经过了多少时间,并在达到某个阈值(此处:3 秒)后增加
count
。
同步更改变量意味着它们将在间隔回调期间拥有新值,这与使用超时不同,在回调之后更改它们。 - 增加
count
当且仅当flag
通过该超时回调从1
更改为0
。更远:- 始终创建超时。无论如何,它们内部的检查只负责增加
count
一次。 - 仅创建一次超时。这将需要一个新变量。
- 始终创建超时。无论如何,它们内部的检查只负责增加
- 在间隔回调之外创建超时。但是,这意味着我们无法检查何时从我们的时间间隔内启动它,因为它是在外部立即创建的。
1。跟踪时间
为了跟踪时间,我们需要引入一个新变量,该变量在我们的间隔回调结束时随着间隔的延迟而增加。
现在我们只需要根据阈值(此处:3000
毫秒)检查经过的时间,并在满足该条件后执行特定操作。
我们可以通过允许启用/禁用 if 块来进一步扩展我们的代码。这可以使用布尔值简单地完成。
let count = 1;
let flag = 1;
let timePassed = 0; // Tracks passed time
let isStoppingEnabled = true; // Allows toggling of the stopping if-block
// Re-starting the timer requires re-setting both variables above; see last arrow expression below
setInterval(() => {
if (flag == 1) {
// Runs once 'timePassed' reaches its threshold of 3000
// and this if-block is "enabled" (see 'isStoppingEnabled')
if (isStoppingEnabled && timePassed >= 3000) {
// Will "disable" _this_ if-block;
// disabling outer if-block using 'flag = 0;' also works
isStoppingEnabled = false;
count++;
}
}
console.log(count);
timePassed += 500; // Remember to increase 'timePassed' by the interval's delay
},500);
document.querySelector('#re-stop').addEventListener('click',() => {
isStoppingEnabled = true; // Re-enable said if-block
});
document.querySelector('#re-stop-track').addEventListener('click',() => {
isStoppingEnabled = true; // Re-enable said if-block and...
timePassed = 0; // re-start time tracking
});
body { /* Ignore; better styling */
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem 0.5rem;
}
<button id="re-stop">Re-enable stopping</button>
<label for="re-stop">
Since 'timePassed' will most likely be already<br>
over its threshold of 3000,'count' will be increased almost immediately.
</label>
<button id="re-stop-track">Re-enable stopping and re-set time-tracker</button>
<label for="re-stop-track">
Will re-set 'timePassed',effectively re-starting<br>
the 3 second timer until 'count' is increased.
</label>
注意:警惕整数“溢出”! JavaScript 不会警告 unsafe integers 的使用!确保将时间跟踪变量保持在整数的安全范围内。
2.使用 setTimeout()
我们可以创建超时来检查它们是否应该使用一个简单的 if 语句执行。
2.1 允许多次超时
当 flag
设置为 1
时将运行的简单程序如下所示:
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
setTimeout(() => {
if (flag == 1) {
flag = 0;
count++;
}
},3000);
}
console.log(count);
},500);
然而,这将创建超时只要创建的第一个超时没有运行,因为在那之前,if语句被执行,创建更多的超时。从技术上讲,这会正常工作,但实际上,它会产生不必要的超时,产生不必要的开销。
2.2 限制超时创建
我们可以使用一个布尔值来限制一次超时的数量,因为我们只想知道一个是否已经创建。
let count = 1;
let flag = 1;
// Prefixed with "_" (underscore) because it has
// no further relevance outside of this script; should actually be private
let _isTimeoutCreated = false;
setInterval(() => {
if (flag == 1) {
if (!_isTimeoutCreated) { // Only create a timeout when none was created before
_isTimeoutCreated = true; // Dis-allow creation of new timeout
setTimeout(() => {
_isTimeoutCreated = false; // Allow creation of new timeout
flag = 0;
count++;
},3000);
}
}
console.log(count);
},500);
3.在 setInterval()
之外创建超时
最简单的方法是在创建间隔后创建单个超时。
两者几乎同时创建,因为超时和间隔是异步的,允许进一步执行当前调用。
如果不是,那么脚本将卡在该行,使您的网站无响应(因为渲染和脚本执行共享同一个线程)。
它们是异步的(这里的意思是:“非阻塞”)意味着,当间隔执行时,超时的计时器仍然会倒计时,在间隔创建 3 秒后执行。
这个解决方案的问题是,如果您想在开始循环后立即开始停止过程,它只会是一个可行的解决方案。您可以从您的时间间隔内开始一个新的超时,但这会使您再次使用点 2.1 或点 2.2。
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
console.log(count);
}
},500);
setTimeout(() => {
flag = 0;
count++;
console.log(count);
},3000);
尾注
显然,还有其他方法可以做到这一点,比如清除间隔并创建一个新的间隔来启动/停止它。这可以包装在实用程序类中。或者可以使用现有库的计时器。或者人们可以找到一种我无法想到的方法。
最后一个重要的注意事项是始终考虑您产生的开销,尤其是在性能很重要的环境中。 但是(!)不应该仅仅因为性能原因而改变他的方式到不同的实现,如果它们不是必需的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。