I am trying to set up intervals with setTimeout() and it works the first time the code is executed. The second time (after a new interval is created), the setTimeouts (that are pushed to intervalTimeouts) are somehow delayed. The code before the setTimeout (rep--; and so on) is instead executed twice, then the setTimeout is repeated twice, simultaneously. The third time the function is called, the code before the setTimeout repeats three times before the timeouts start. I tried removing the delay before the function calls itself (by changing setTimeout(intervalRep, pause * 1000); to intervalRep();) but this does not solve the problem. Does anyone have a clue as to why this is happening?
function intervalRep() {
if (intervalsRunning === true) {
rep--;
document.body.style.background = '#FB2843';
calcSprintSegments(sprint * 1000);
longBeep.play();
intervalTimeouts.push(setTimeout(function() {
if (rep > 0) {
document.body.style.background = '#BB39F0';
lowBeep.play();
setTimeout(intervalRep, pause * 1000);
} else {
intervalsRunning = false;
intervalTimeouts.forEach(function(t) {
clearTimeout(t);
});
intervalTimeouts = [];
document.body.style.background = '#4A3DF9';
cheering.play();
}
}, sprint * 1000)
);
}
}
Related
function x() {
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
console.log("Hello World!!");
}
x();
The above function produces the output 1 to 5 but it's taking 1 second to produce each output. And that's what I am not getting... Why is it taking 1 second each and why not 1 second for first value of i, then 2 seconds for second value of i and so on and so forth as I am multiplying the milliseconds with the i value?
The main confusion you are having is pretty common and comes from the fact that you are using a loop. Everything outside of the timer callback is JavaScript that is being executed synchronously with no delay. The loop executes 5 times immediately when you run the code and so 5 instances of the timer callback function get placed on the event queue in a matter of milliseconds and it is from that point in time that all 5 timer callbacks are delayed, rather than one callback being delayed from the completion of the prior one.
The first instance of the callback then runs after its initial delay is reached (1 second), then the second instance runs after its delay (2 seconds, which is only 1 second later than when the first function call ran) and then the third one runs (which is only 1 second behind the previous one of 2 seconds) and so on.
What you need to do is place the first instance of the callback on the event queue with a 1 second delay before it runs and then, when that first instance has completed, place another instance on the event queue with a 2 second delay, and so on.
To do this, forget the loop and make the timer function recursive, which will essentially cause a repeating code call, just as a loop would.
let delay = 1000;
let timer = null; // Will hold a reference to the timer
function x() {
timer = setTimeout(function(){
console.log(delay / 1000);
delay += 1000;
if(delay > 5000){
clearTimeout(timer); // Cancel the timer
console.log("Operation Complete!");
} else {
// Because the next call for the parent function comes from within
// the timer callback function, it is delayed until the end of that
// callback function's execution.
x();
}
}, delay);
}
x();
If you want the behaviour of adding a i seconds each time, do something like:
function x(i, max = Infinity) {
if (i > max) return
console.log("Hello World!! %s", i);
setTimeout(function() {
console.log(i);
x(i + 1, max)
}, i * 1000);
}
x(1, 5);
var seconds_lapsed = 0;
function tick() {
seconds_lapsed++;
}
function countup() {
setTimeout(function () {
if (stopped) return; // stop the loop
if (!is_paused()) {
tick();
show_time_left();
}
countup(); // <--- this is the "loop"
}, 1000);
}
This is the core of my timer. Of course I have some view to represent the result. But ticking is done here.
The problem
It shows wrong time. Have a look at this:
The timer was set for 3 hours. Twelve minutes lapsed. And the discrepancy is almost 1.5 minutes.
In the other window the timer by Google is working. This one:
So, I just compared my timer with that of google. I started them almost at once. The difference should be no more than a couple of seconds (to switch the window and press the button).
What is the reason for this, and how can I correct it?
setTimeout with an interval of 1000 does NOT run exactly after every 1 seconds.
It schedules to run after 1 second, but can be delayed with by actions running at that time.
A better way of solving this is by calculating via date difference.
I took your sample (added the missing vars/funcs) and changed the tick() function to use date diffs.
var seconds_lapsed = 0;
var startDateTime = new Date();
var stopped = false;
var is_paused = function() { return false; }
function tick() {
datediffInms = new Date() - startDateTime;
seconds_lapsed = Math.round(datediffInms / 1000);
}
function countup() {
setTimeout(function () {
if (stopped) return; // stop the loop
if (!is_paused()) {
tick();
//show_time_left();
console.log(seconds_lapsed)
}
countup(); // <--- this is the "loop"
}, 1000);
}
countup();
I am trying to get a function to run 10 times with a pause inbetween each run, yet when I try to it repeats the function infinite times then after 10 times it pauses, and so on. Right now this is the code with the problem:
for(i=0;i<10;i++) {
console.log(i);
interval = setInterval(function() {console.log("Function ran");}, 1000);
}
window.clearInterval(interval);
Console:0123456789Function ran["Function ran" is repeated infinite times after "9"]
interval = setInterval(function() {console.log("Function ran");}, 1000);
This line creates a new interval-instance each time, which means you have created 10 intervals. At the end of the loop interval holds the id of the last interval that was created. Hence that's the only one you're clearing, and the other ones are still running.
To cancel the interval, you need to keep track of how many times the function has been invoked. One way you can do that is as follows:
function pauseAndRepeat(delay, iterations, func) {
var i = 0;
var interval = setInterval(function() {
func();
if(++i === iterations) {
clearInterval(interval);
}
}, delay);
}
Here we have a function that defines a counter (i) in its local scope. Then it creates an interval using a function that checks the counter to see if it should call your function (func) or clear the interval when it is done. interval will have been set when the interval-handler is actually called. In this case the handler is basically a closure since it is bound to the local scope of pauseAndRepeat.
Then you can invoke the function as follows:
pauseAndRepeat(1000, 10, function() {
console.log("Function ran");
});
This will print out Function ran ten times, pausing for a second each time.
setInterval is expected to run forever, on an interval. Every time you call setInterval here, you have a new infinite loop running your function every 10s, and as others have noted you only are canceling the last one.
You may do better with chained setTimeout calls:
var counter = 0;
function next() {
if (counter < 10) {
counter++;
setTimeout(function() {
console.log("Function ran");
next();
}, 1000);
}
}
next();
This chains delayed functions, setting a timeout for the next one after each runs. You can do something similar with setInterval and cancellation:
var counter = 0;
var intervalId = setInterval(function() {
console.log("Function ran");
if (++counter >= 10) {
clearInterval(intervalId);
}
}, 1000);
In both these cases the key issue is that you trigger the next run or cancel the interval within the callback function, not in synchronous code.
I have a function in JavaScript. I use setInterval in order to control my function. I also have another logic for controlling my function. I have a counter which is increased once one condition happens and is decreased when another condition happens. Now, sometimes the second condition does not happen and hence my function won't be resume anymore. (I pause my function when my first condition happen). Therefore, I want to wait at most 30 seconds for the second condition. If it does not happen, then I want to resume my function anyway. I have following code, but it does not work as I expect it. What happens is that it resume my function every 30 seconds. Then, it may be resumed while it should wait. Can someone let me know what is the problem with my code?
Please note that, the value for the counter may increase to more than 20. I mean the first and second condition may occur more than once.
function main()
{
// body
}
function increaseCounter()
{
counter += 1;
clearInterval(controller);
controlSecond = setInterval(function(){
counterSeconds += 1;
if (counterSeconds == 30)
{
counterSeconds = 0;
controller = setInterval(main, 100);
clearInterval(controlSecond);
}
}, 1000);
}
function decreaseCounter()
{
counter -= 1;
if (counter == 0)
{
counterSeconds = 0;
clearInterval(controlSecond);
controller = setInterval(main, 100);
}
}
Consider what happens if you call increaseCounter twice in a row.
On the first execution it will create interval A and assign it to controlSecond.
On the second execution it will create interval B and assign it to controlSecond, while interval A continues to fire off indefinitely. You won't stop it with clearInterval(controlSecond) because controlSecond no longer references interval A.
The problem is that you continue to set controlSecond and controller to a new interval without clearing them first. That results in the intervals being leaked with no way of clearing them. It's sort of like a memory leak where you have dynamically allocated memory but nothing pointed at it, but instead of renegade memory you have renegade intervals.
One way to prevent this is to make sure you always clear your interval before setting it.
I would also recommend that you implement controlSecond with a setTimeout because that is designed for tasks which only happen once.
Why not
var counter = 0
var timeout = null
function main () {
clearTimeout(timeout);
timeout = null;
}
function increaseCounter () {
counter++;
if (!timeout)
timeout = setTimeout(main, 30*1000);
}
function decreaseCounter() {
counter--;
if (counter === 0)
main();
}
I have been looking for answers for hours and it seems that my problem is with closure. I could not figure out how to fix it in my scenario. I want to trigger an animation onload on 6 elements, all 1000ms apart. I can't get the setTimeout method to work properly. Please help!
$(document).ready(function() {
we();
});
var data = ['450px', '300px', '200px', '120px', '250px', '320px'];
var bars = $('.bar');
var i = 0;
function grow(size, elem) {
$(elem).animate({
height: size,
opacity: '1.0'
}, 1000);
}
function we() {
setTimeout (function(){ // if I don't use an annonymous function call here, they all animate at the same time
grow(data[i], bars[i]);
}, 1000);
if (i >= bars.length) {
return;
} else {
i++;
return we();
};
}
You have to put your setTimeout around the rest of your function as well; not just the call to grow.
function we() {
setTimeout (function(){
grow(data[i], bars[i]);
if (i >= bars.length) {
return;
} else {
i++;
return we();
};
}, 1000);
}
I think what you've missed is that setTimeout does not block your program. It doesn't stop execution, wait for 1000 ms and then continue. Instead, it will delay the execution of the anonymous function for 1000 ms, but the rest of the program will continue to execute right away. And that part of your program will call we once again, and schedule a new animation (almost at the same time as the last one). And then another and so on.
By putting the recursive call inside setTimeout, we delay the recursion as well and hence all future animation steps.