setTimeout in JS - javascript

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);

Related

How to make setInterval() to wait for an interval (like 10 seconds) before EVERY iteration?

The normal behavior of setInterval(function() {...}, intervalInMilliseconds) in Javascript is such that it is called for the first time after intervalInMilliseconds milliseconds, and after that continues to run again and again without any delay or wait.
I need my code to be executed after every, say, 10 seconds. The following will execute the function for the first time after 10 seconds and then continue to execute it again and again (until clearInterval() is called) without any delay/wait.
setInterval(function() {
//code, e.g. some AJAX request
}, 10000);
I need each iteration to be executed after a 10 seconds delay. How can I do that?
So, as I understand it, you want the "loop" to execute 10 seconds after your code has completed? If so, you could do something like this...
Execute your synchronous long running code
Execute another loop, 10 seconds later (I've used 1 second in the example)
Synchronous example...
var loop = () => {
setTimeout(() => {
doSyncWork()
loop()
}, 1000)
}
loop()
Or asynchronously (e.g. ajax)...
var loop = () => {
setTimeout(() => {
doAsyncWithCallback(loop)
}, 1000)
}
loop()
Or with a Promise...
var loop = () => {
setTimeout(() => {
doAsyncWithPromise().then(loop)
}, 1000)
}
loop()
The normal behavior of setInterval(function() {...},
intervalInMilliseconds) in Javascript is such that it is called for
the first time after intervalInMilliseconds milliseconds, and after
that continues to run again and again without any delay or wait.
thats not true.
mormal behavior of setInterval is to execute callback every n milliseconds.
every callback call will occur after previous callback invocation over n milliseconds.
take a look
let counts = 0;
let interval = setInterval(() => {
console.log(counts % 2 == 0 ? 'tick' : 'tack');
counts++;
if (counts == 5)
clearInterval(interval);
},1000);
I need each iteration to be executed after a 10 seconds delay. How can
I do that?
like this:
setInterval(callback, 10000);
every callback will be called each ten seconds

setInterval being skipped the first call and Clearinterval not stopping when called Javascript

I am trying to create a timer that every second it does calls another function. So I am using setInterval within a for loop.
The setInterval is being skipped during the first loop through in the for loop.
Not sure why.
Also,
I have the function to clear the Interval when it goes below zero. When I have a message printing within the stopping condition, that outputs but the clearInterval is skipped.
function changesystem(lottery, maxroundtime, firstloop) {
//loop through lottery numbers
for (var keys in lottery) {
var currentnum = lottery[keys].LotteryNum;
console.log(currentnum);
var currentclass = lottery[keys].ClassID;
//console.log(currentclass);
//display values
$('#CurrentNumber').text(currentnum);
$('#CurrentClass').text(currentclass);
//change progress bar
//every second until reaches max round time
// var loopcontrol = maxroundtime;
var loopcontrol = 5;
var timerloop = setInterval(function() {
console.log(loopcontrol);
//changetime(maxroundtime,firstloop);
loopcontrol--;
//firstloop=1;
}, 1000);
if (loopcontrol < 0) {
clearInterval(timerloop);
}
}
Visual Example
You need to take into account that asynchronous code does not stop the current execution-code. The callback function you pass to setInterval is called asynchronously, i.e. after the currently being executed code has finished until the point the call-stack is empty.
So setInterval does not wait somehow for the seconds to pass. The for loop will go through all iterations immediately, having created just as many setInterval timers, which all start to tick afterwards, and will continue to do so for ever. The clearInterval code will never get executed.
Here is how you could do it, making use of asynchronous "loops", i.e. with functions that that get called several times, asynchronously.
function changesystem(lottery,maxroundtime,firstloop){
//loop asynchronously through lottery numbers
(function loop(keys) {
if (!keys.length) return; // all done
var key = keys.shift(); // extract next key
var currentnum = lottery[key].LotteryNum;
console.log('lottery number', currentnum);
var currentclass = lottery[key].ClassID;
//display values
$('#CurrentNumber').text(currentnum);
$('#CurrentClass').text(currentclass);
//change progress bar asynchronously
//every second until reaches max round time
(function progress(loopControl) {
console.log('loopControl', loopControl);
if (!loopControl) {
loop(keys); // done: continue with next lottery number
} else { // schedule next progress tick
setTimeout(progress.bind(null, loopControl-1), 1000);
}
})(5); // start with loopControl 5
})(Object.keys(lottery)); // pass all keys
}

interval keeps firing even though clearInterval has been called

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.

How to skip one step in setInterval jquery

How to skip a one step in jquery setInterval function
e.g
<script>
// start updating continuously
var timer, delay = 3000; // time in milli seconds
timer = setInterval(function(){
// do something for each iteration
// I want to do this only once
if(result["pass"]){
$("#test").append("<li>Passed</li>");
}
// do something for each iteration
}, delay);
</script>
how can I skip one or more than one steps to happen if they are happened once.
I want to skip only when it happens once. e.g if the condition is true in 101st iteration then it will not happen in first 100 iterations but if condition is still true in 102nd iteration, it should not happen because it happens in 101st iteration.
Any help would be much appreciated.
<script>
// start updating continuously
var timer, delay = 3000; // time in milli seconds
var alreadyAdded=false;
timer = setInterval(function(){
// do something for each iteration
// I want to do this only once
if(!alreadyAdded && result["pass"]){
$("#test").append("<li>Passed</li>");
alreadyAdded=true;
}
// do something for each iteration
}, delay);
</script>
This is a good example of where you can use a closure. You can create a function that returns a function.
This allows you to declare a variable in the scope of the outer function, which can then be accessed by the inner function.
function getIntervalHandler() {
var hasPassed = false;
return function() {
if(!hasPassed) {
if(result["pass"]){
$("#test").append("<li>Passed</li>");
hasPassed = true;
}
}
};
}
timer = setInterval(getIntervalHandler(), delay);

Accurately run a function when the minute changes?

How could I accurately run a function when the minute changes? Using a setInterval could work if I trigger it right when the minute changes. But I'm worried setInterval could get disrupted by the event-loop in a long-running process and not stay in sync with the clock.
How can I run a function accurately when the minute changes?
First off, you should use setInterval for repeating timers, since it (tries to) guarantee periodic execution, i.e. any potential delays will not stack up as they will with repeated setTimeout calls. This will execute your function every minute:
var ONE_MINUTE = 60 * 1000;
function showTime() {
console.log(new Date());
}
setInterval(showTime, ONE_MINUTE);
Now, what we need to do is to start this at the exact right time:
function repeatEvery(func, interval) {
// Check current time and calculate the delay until next interval
var now = new Date(),
delay = interval - now % interval;
function start() {
// Execute function now...
func();
// ... and every interval
setInterval(func, interval);
}
// Delay execution until it's an even interval
setTimeout(start, delay);
}
repeatEvery(showTime, ONE_MINUTE);
This may be an idea. The maximum deviation should be 1 second. If you want it to be more precise, lower the milliseconds of setTimeout1.
setTimeout(checkMinutes,1000);
function checkMinutes(){
var now = new Date().getMinutes();
if (now > checkMinutes.prevTime){
// do something
console.log('nextminute arrived');
}
checkMinutes.prevTime = now;
setTimeout(checkChange,1000);
}
1 But, see also this question, about accuracy of timeouts in javascript
You can try to be as accurate as you can, setting a timeout each X milliseconds and check if the minute has passed and how much time has passed since the last invocation of the function, but that's about it.
You cannot be 100% sure that your function will trigger exactly after 1 minute, because there might be something blocking the event-loop then.
If it's something vital, I suggest using a cronjob or a separate Node.js process specifically for that (so you can make sure the event loop isn't blocked).
Resources:
http://www.sitepoint.com/creating-accurate-timers-in-javascript/
I've put up a possible solution for you:
/* Usage:
*
* coolerInterval( func, interval, triggerOnceEvery);
*
* - func : the function to trigger
* - interval : interval that will adjust itself overtime checking the clock time
* - triggerOnceEvery : trigger your function once after X adjustments (default to 1)
*/
var coolerInterval = function(func, interval, triggerOnceEvery) {
var startTime = new Date().getTime(),
nextTick = startTime,
count = 0;
triggerOnceEvery = triggerOnceEvery || 1;
var internalInterval = function() {
nextTick += interval;
count++;
if(count == triggerOnceEvery) {
func();
count = 0;
}
setTimeout(internalInterval, nextTick - new Date().getTime());
};
internalInterval();
};
The following is a sample usage that prints the timestamp once every minute, but the time drift is adjusted every second
coolerInterval(function() {
console.log( new Date().getTime() );
}, 1000, 60);
It's not perfect, but should be reliable enough.
Consider that the user could switch the tab on the browser, or your code could have some other blocking tasks running on the page, so a browser solution will never be perfect, it's up to you (and your requirements) to decide if it's reliable enough or not.
Tested in browser and node.js
sleeps until 2 seconds before minute change then waits for change
you can remove logging as it gets pretty cluttered in log otherwise
function onMinute(cb,init) {
if (typeof cb === 'function') {
var start_time=new Date(),timeslice = start_time.toString(),timeslices = timeslice.split(":"),start_minute=timeslices[1],last_minute=start_minute;
var seconds = 60 - Number(timeslices[2].substr(0,2));
var timer_id;
var spin = function (){
console.log("awake:ready..set..");
var spin_id = setInterval (function () {
var time=new Date(),timeslice = time.toString(),timeslices = timeslice.split(":"),minute=timeslices[1];
if (last_minute!==minute) {
console.log("go!");
clearInterval(spin_id);
last_minute=minute;
cb(timeslice.split(" ")[4],Number(minute),time,timeslice);
console.log("snoozing..");
setTimeout(spin,58000);
}
},100);
};
setTimeout(spin,(seconds-2)*1000);
if (init) {
cb(timeslice.split(" ")[4],Number(start_minute),start_time,timeslice,seconds);
}
}
}
onMinute(function (timestr,minute,time,timetext,seconds) {
if (seconds!==undefined) {
console.log("started waiting for minute changes at",timestr,seconds,"seconds till first epoch");
} else {
console.log("it's",timestr,"and all is well");
}
},true);
My first thought would be to use the Date object to get the current time. This would allow you to set your set interval on the minute with some simple math. Then since your worried about it getting off, every 5-10 min or whatever you think is appropriate, you could recheck the time using a new date object and readjust your set interval accordingly.
This is just my first thought though in the morning I can put up some code(its like 2am here).
This is a fairly straightforward solution ... the interval for the timeout is adjusted each time it's called so it doesn't drift, with a little 50ms safety in case it fires early.
function onTheMinute(callback) {
const remaining = 60000 - (Date.now() % 60000);
setTimeout(() => {
callback.call(null);
onTheMinute(callback);
}, remaining + (remaining < 50 ? 60000 : 0));
}
Here's yet another solution based on #Linus' post and #Brad's comment. The only difference is it's not working by calling the parent function recursively, but instead is just a combination of setInterval() and setTimeout():
function callEveryInterval(callback, callInterval){
// Initiate the callback function to be called every
// *callInterval* milliseconds.
setInterval(interval => {
// We don't know when exactly the program is going to starts
// running, initialize the setInterval() function and, from
// thereon, keep calling the callback function. So there's almost
// surely going to be an offset between the host's system
// clock's minute change and the setInterval()'s ticks.
// The *delay* variable defines the necessary delay for the
// actual callback via setTimeout().
let delay = interval - new Date()%interval
setTimeout(() => callback(), delay)
}, callInterval, callInterval)
}
Small, maybe interesting fact: the callback function only begins executing on the minute change after next.
The solution proposed by #Linus with setInterval is in general correct, but it will work only as long as between two minutes there are exactly 60 seconds. This seemingly obvious assumption breaks down in the presence of a leap second or, probably more frequently, if the code runs on a laptop that get suspended for a number of seconds.
If you need to handle such cases it is best to manually call setTimeout adjusting every time the interval. Something like the following should do the job:
function repeatEvery( func, interval ) {
function repeater() {
repeatEvery( func, interval);
func();
}
var now = new Date();
var delay = interval - now % interval;
setTimeout(repeater, delay);
}

Categories

Resources