setInterval go faster every time it is called - javascript

This time go faster if is called 2 times, 3 times faster and so on.
function startIdleTime() {
var ciclo;
function startSlidercicle() {
ciclo = setInterval( function() {
let seconds = parseInt(sessionStorage.getItem('idle'));
seconds += 1;
sessionStorage.setItem('idle', seconds);
}, 1000);
}
// start idle counter
if (!sessionStorage.getItem('idle')) {
alert('non esiste timer, lo imposto')
sessionStorage.setItem('idle', 0);
alert(3)
}
if (sessionStorage.getItem('idle') > 15) {
anotherFunction();
}
if (sessionStorage.getItem('idle') < 15 || !sessionStorage.getItem('idle')) {
clearInterval(ciclo);
startSlidercicle();
}
}
I need to set idle time. When 15 i'll call an other function,
if <= 15 I reset only a counter to 0
But if is called few times my count go faster then 1sec }, 1000);)

Looks like the interval is not cleared before instantiating a new one. The result will be several intervals that will be executed with a different phase, and it will look like it's running with a shorter interval.
The reason for this behavior is that you are not clearing the interval, because you are creating a new ciclo variable every time you call the startIdleTime function. Probably you want the variable ciclo to be global, in order to share the interval handler across the function calls. You need to widen the scope, and to widen the scope you can just move the variable declaration out of the function definition:
var ciclo;
function startIdleTime() {
function startSlidercicle() {
Also note that in the following line:
sessionStorage.getItem('idle') < 15 || !sessionStorage.getItem('idle') 
the second part is never evaluated. Let's suppose that getItem returns null: then null < 15 is true, so the check becomes useless

Related

setTimeout being called multiple times

I can't figure out why setTimeout is being called multiple times in my code.
Here's a snippet of the code with what I thought was irrelevant removed:
let dead;
setup()
{
dead = false;
}
draw()
{
if(fell == true)
{
dead = true;
}
mechanics();
}
function mechanics()
{
let triggerVar;
if(dead == true)
{
triggerVar = 1;
dead = false;
}
if(triggerVar == 1)
{
setTimeout(resetG, 1500);
triggerVar = 0;
}
}
function resetG()
{
lives -= 1;
position = 0;
}
I can't tell what I'm doing wrong because whenever the character dies and setTimeout is called, it is actually not only called after the delay but also for the exact same duration after it is triggered. So in this case it is triggered first after 1500 millis and then every frame for another 1500 millis.
I managed to find the problem, which was not with the code I posted. The problem was that the constructor code that makes the object that changes dead to true if certain conditions are met was being called every frame from the moment it triggered death until the first instance of setTimeout kicked in, which means setTimeout was called every frame for 1500 milliseconds.
Chances are that you mechanics() function is called multiple times, you may give a variable to the settimeout like:
let timeoutID= setTimeout(resetG, 1500);
And in the proper place to clear it, for example after lifecycle
clearTimeout(timeoutID);

Counter in set Timeout nested in set interval is summed multiple times per callback

I have some code which is intended to update a counter that's inside of a set interval block. My intent is to have the counter updated once every time the set timeout function is called. But it seems that the callback for setTimeout is active for the entire duration of the timeout, so the counter is summed repeatedly over the 3 second timeout period, resulting in it having an end value of 8 rather than the desired value of 2.
I'm trying to understand how to implement this where the counter will be called immediately after the timeout, and only once. Unfortunately simply putting it after the function does not seem to solve this issue.
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
setTimeout(()=>{
flag = 0;
count++;
}
,3000);
}
},500);
that ?
let count = 1
, flag = 1
, noTimCall = true
;
setInterval(() =>
{
if (flag === 1 && noTimCall)
{
noTimCall = false
setTimeout(()=>
{
flag = 0
count++
noTimCall = true
}
,3000)
}
}
,500)
Let's go through your code step by step.
0ms since start. flag is 1 ⇒ 1st timeout created.
500ms since start. flag is 1 ⇒ …
…
3000ms since start. flag is 1 ⇒ 7th timeout created.
1st timeout is executed: Setting flag to 0, increasing count to 2.
3500ms since start. flag is 0 ⇒ Effectively nothing.
2nd timeout executed: …, increasing count to 3.
4000ms since start. flag is 0 ⇒ …
3rd timeout executed: …
…
6000ms since start. …
7th timeout executed: …, increasing count to 8.
As you can see, there will be 7 timeouts created, each one increasing count by one, ending with count at 8.
There are multiple ways to increase count only once after a delay:
Not using timeouts. We can keep track of how much time has passed manually, and increase count once a certain threshold (here: 3 seconds) is reached.
Changing the variables synchronously means they will already have their new values during the interval-callback, unlike using timeouts where they are changed after the callback.
Increase count if and only if flag is changed from 1 to 0 by that timeout-callback. Further:
Always create timeouts. The check inside them takes care of incrementing count only once, anyway.
Only create one timeout. This would require a new variable.
Create a timeout outside of the interval-callback. However, this means we cannot check when to start it from within our interval, as it is created outside immediately.
1. Keeping track of time
To keep track of time, we need to introduce a new variable that is increased by the interval's delay at the end of our interval-callback.
Now we only need to check the passed time against a threshold (here: 3000 milliseconds), and do a certain action once that condition is met.
We can further extend our code by allowing the if-block to be en-/disabled. This can be simply accomplished using a boolean.
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>
Note: Be wary of integer "overflows"! JavaScript won't warn about the usage of unsafe integers! Make sure to keep your time-tracking variable in the safe range of integers.
2. Using setTimeout()
We can create timeouts that will check whether they should execute or not using a simple if-statement.
2.1 Allow multiple timeouts
A simple one that will run when flag is set to 1 would look like this:
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
setTimeout(() => {
if (flag == 1) {
flag = 0;
count++;
}
}, 3000);
}
console.log(count);
}, 500);
However, that would create timeouts as long as the first timeout created hasn't run, since until then, the if-statement is executed, creating further timeouts. Technically, this will work just fine, but actually, it will create unnecessary timeouts producing unnecessary overhead.
2.2 Restricted timeout creation
We can restrict the amounts of timeouts at a time using a single boolean, since we only want to know if one was already created or not.
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. Creating a timeout outside of setInterval()
The simplest way would be to create a single timeout after creating the interval.
Both will be created at almost the same time because timeouts and intervals are asynchronous, allowing further execution of the current call.
If they weren't, then the script would be stuck at that line, making your website unresponsive (because rendering and script-execution share the same thread).
Them being asynchronous (here meaning: "non-blocking") means, that while the interval executes, the timeout's timer will still tick down, executing after 3 seconds of your interval's creation.
The problem with this solution is, that it will only be a viable solution if you want to start the stopping process right away after starting your loop. You can start a new timeout from within your interval, but that would make you use either point 2.1 or point 2.2 again.
let count = 1;
let flag = 1;
setInterval(() => {
if (flag == 1) {
console.log(count);
}
}, 500);
setTimeout(() => {
flag = 0;
count++;
console.log(count);
}, 3000);
Endnote
Obviously, there are further ways to do this, like clearing the interval and creating a new one for starting/stopping it. That could be wrapped in a utility-class. Or one could use a Timer of an already existing library. Or one could find a way I couldn't think of at the top of my head.
An important last note would be to always think about the overhead you produce, especially in environments where performance matters. But(!) one shouldn't change his way to a different implementation only because of performance reasons, if they aren't necessary.

setInterval in function keeps speeding and clear interval is ignored

When I activate showMoves() the setInterval is supposed to repeat the function one second at a time.
However, it speeds up after a few seconds and activates the function several times in a single second instead of once. Also the clearInterval isn't working even though the if statement for it turns true.
let i = -1;
function showMoves() {
const start = setInterval(showMoves, 1000);
if (i > game.computerMoves.length) {
clearInterval(start);
}
console.log(i + ' ' + game.computerMoves.length);
const showColors = new Map([
[green, 'lime'],
[yellow, 'rgb(255,255,102)'],
[blue, 'dodgerblue'],
[red, 'salmon'],
]);
i++;
let move = game.computerMoves[i];
move.style.backgroundColor = showColors.get(move);
}
You shouldn't be calling setInterval inside the very same function that's being called by setInterval -- you're setting up multiple interval timers that way, not just one that you can clear away easily. It's "speeding up" as you say because you're seeing more and more different interval timers calling the same function.
You're recursively calling showMoves - every second, a new interval is created. When the function runs and the stop condition isn't reached, the start reference you have to the interval you just created is just garbage collected. You want a method to save the intervals so that you can reference them later so they can be cleared. For example:
let intervals = [];
function showMoves() {
intervals.push(setInterval(showMoves, 1000));
if (i > game.computerMoves.length) {
// clear all currently running intervals:
intervals.forEach(clearInterval);
intervals = [];
}
// ...

setTimeout not waiting specified time

My setTimeout statements are making function calls instantly instead of waiting the time I specified and I'm not sure why. It should take 10 seconds for the for loop and then 110 seconds to change a boolean value from T to F.
for(var c = 0; c < 10; c++)
{
setTimeout(function()
{
gold = gold + clickerPower;
$("#totalGold").html("Gold: " + gold);
console.log("Clicked!");
}, 1000);
}
setTimeout(function(){frenzyActive = false;}, 110000);
Starting a timeout is an asynchronous operation. setTimeout accepts a function as it's first argument because it's registering that callback function to be invoked at a later time. Every iteration of the JavaScript event loop it checks to see if the appropriate time has passed and if so then it fires the registered callback. Meanwhile it's still moving on to run other code while it waits.
Your for loop is not waiting for anything. It iterates to 10 super fast and all you've done is register ten callbacks to fire all at the same time in exactly one second (the time is specified in milliseconds by the way, so 1000 = 1 second).
You need setInterval.
var count = 0;
var intervalId = setInterval(function () {
gold = gold + clickerPower;
$('#totalGold').html('Gold: ' + gold);
console.log('Clicked!');
count++;
if (count === 10) {
clearInterval(intervalId);
frenzyActive = false;
}
}, 1000);
That function will run once every second and increment a count variable each time. When it reaches 10 we call clearInterval and give it the intervalId returned from setInterval. This will stop the interval from continuing.
Take a gander at this post I wrote back when I too was confused about asynchronous callbacks :)
http://codetunnel.io/what-are-callbacks-and-promises/
I hope that helps :)
Good luck!
It will not take 10 seconds to execute the loop. Look will execute immediately and the anonymous function will be enqueued 10 times to be executed after 1 second.
The last call to setTimeout will cause the anonymous function to be executed after 110 seconds.
To ensure that the anonymous function within the loop is called sequentially, 10 times, after a gap of 1 second each, do the following:
var c = 0;
setTimeout(function() {
if(c < 10) {
gold = gold + clickPower;
console.log("Clicked");
c++;
setTimeout(arguments.callee, 1000);
//^^ Schedule yourself to be called after 1 second
}
}, 1000);
I did come across this challenge today, and the answer provided by #Guarav Vaish set me on a path to success. In my case, I am using ES6 syntax and its worth noting that the arguments.callee is deprecated. However, here is how I'd write the same solution as contributed by Vaish:
var c = 0;
setTimeout(function named() {
if(c < 10) {
gold = gold + clickPower;
console.log("Clicked");
c++;
setTimeout( ()=>{ named() }, 1000);
//^^ Schedule yourself to be called after 1 second
}
}, 1000);
Note: I am simply using a named function in place of the anonymous one in the original solution. Thank you. This was really helpful

Execute interval callback after every 5th call

I have an interval running every 6 seconds on my page; it populates a bunch of data really nicely.
However, there is one piece of data that is only updated from the DB every 30 seconds. So, I want to wait to execute this callback because if it executes beforehand, it wipes everything out.
My code below waits 30 seconds and then checks. What I'd like is the opposite: Check immediately and then wait 30 seconds before checking again.
I feel like my code is cloogy below and there's a much more elegant solution (though I don't think a do/while loop fits the bill). Am I wrong?
counter = 0;
maxCounts = 5;
function callbackForInterval(){
if(counter === maxCounts){
if({data hasn't changed}){
// wipe everything out!!!
}else{
// do some other stuff
}
counter = 0;
}
counter++;
}
Thanks for any helpful hints.
You can use the remainder operator (%).
var counter = 0;
function callbackForInterval)( {
if (counter++ % 5 === 0) {
// This bit only runs every 5 iterations
}
}
That will run the code on the first iteration. To run it only starting with the 5th, change the post-increment to a pre-increment.
Live Example:
var counter = 0;
function callbackForInterval() {
if (counter++ % 5 === 0) {
snippet.log("big tick");
// This bit only runs every 5 iterations
} else {
snippet.log("little tick");
}
}
var timer = setInterval(callbackForInterval, 500);
setTimeout(function() {
snippet.log("done");
clearInterval(timer);
}, 30000);
snippet.log("(stops after 30 seconds)");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Side note: I added var to the above, because unless you're declaring that counter variable somewhere, your code is falling prey to The Horror of Implicit Globals.

Categories