JavaScript - setTimeout doesn't work in a for loop - javascript

for university I have to create a javascript slideshow.
I use the function setTimeout( "other function", timeout).
My problem is that using this function recursive ( slideshow() ) works but when I try use it in a for loop ( altSlideshow() ) nothing happens.
// this one works
function slideShow() {
nextImg();
setTimeout(slideShow, timeOut); //Angabe Timeout in Millisekunden
}
// this one doesn't work
function altSlideshow(){
for(var x = 0; x <= 4; x++){
setTimeout(nextImg(), timeOut);
}
}
Thanks in advance!

Two problems here :
you pass nextImg() instead of the function nextImg
you schedule all the timeouts for the same instant (the delay you pass is computed from the time of execution of the loop)
Change
setTimeout(nextImg(), timeOut);
to
setTimeout(nextImg, timeOut*(x+1));

I think you want to pass a reference to the function, rather than using the returned value:
function altSlideshow(){
for(var x = 0; x <= 4; x++){
setTimeout(nextImg, timeOut);
}
}
Also, as dystroy mentions, you should make use of x to generate a unique timeout for each iteration.

Related

Add delay between script execution, especially in FOR loop

I have a block of script that will produce a set of DOM. It has a loop that contains an asynchronous function (function A) and then function B after it. Function B's effect will be shown if the asynchronous function (A) is completely executed. I tried to use setTimeout function, but it doesn't work well, some of object outside setTimeout could not be accessed inside it.
Here's the script:
var parents = data.hasil.parent.split(">>");
var jmlparent = parents.length;
for (var i = jmlparent-2; i >= 1; i--){
add_parent(); // it's the asynchronous function (function A)
setTimeout(function (){
$("#parent_"+(jmlparent-i)).val(parents[(i-1)]); //(function B)
//returns error: parents is not defined
},200);
//i think 200ms is enough for browser to complete the execution of function A
}
Does anyone have the solution?
Function A should return a promise which when resolved you would call function B. You do not know how long function will take to resolve, therefore setTimeout() is not a good approach.
You also do need a closure:
var parents = data.hasil.parent.split(">>");
var jmlparent = parents.length;
for (var i = jmlparent-2; i >= 1; i--){
(function( i ) {
add_parent().done(function() {
$("#parent_"+(jmlparent-i)).val(parents[(i-1)]);
});
})( i );
}
I did not want to discuss the use of setTimeout but just improve you code. Try this :
...
for (var i = jmlparent-2; i >= 1; i--){
add_parent();
setTimeout(function (a){
$("#parent_"+(jmlparent-a)).val(parents[(a-1)]); //(function B)
},200, i); // we pass here the i var to setTiemout
}
About add_parent() why you call it inside the loop ? may be before the loop once is better ?! I dont know ...

setInterval within a for-loop not working

What I want is an infinite loop that alerts 1, 2, 3, 1, 2, 3, ... with an interval of 2000 milliseconds. But it's not working. The console's not showing any error though. What's the problem here?
for (i = 1; i <= 3; i++) {
setInterval(function() {
alert(i);
}, 2000);
if (i == 3) {
i = 0;
}
}
This will do:
var i = 0;
setInterval(function () {
i += 1;
if (i == 4) {
i = 1;
}
alert(i);
}, 2000);
I've checked it chrome too.
It outputs 1,2,3,1,2,3... as you have requested.
you can not setInterval() inside a for loop because it will create multiple timer instance.
The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).
The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
The ID value returned by setInterval() is used as the parameter for the clearInterval() method.
Tip: To execute a function only once, after a specified number of milliseconds, use the setTimeout() method.
var i = 0
function test() {
i = i % 3;
++i;
alert(i);
};
setInterval('test()', 2000);
You would not need a loop for this, an interval already goes on infinitley. Try this instead:
var i = 1;
setInterval(function() {
alert(i);
i++;
if(i > 3) {
i = 1;
}
}, 2000);
The reason why this is not working is because you enter the infinite loop in a blocking state, meaning that the interval is never entered as the browser is busy looping. Imagine the browser can only do one thing at a time, as in a single thread, so the loop is it, and cannot do anything else until it's done, and in your case it never is, therefore the interval is waiting for it's turn, which it never gets.
You could make it none blocking like this:
function recursion () {
for (var i = 1; i < 4; i++) {
var num = i;
setInterval(function() {
console.log(String(this));
}.bind(num), 2000);
}
recursion ();
}
recursion ();
my best suggestion is . use event monogramming righterthen loop ,
first make a function then after completing of setInterval call to next function and so on.. that's how u can solve this p

Why is this javascript not running as expected?

function animateGraph() {
var graph;
for(i=0; i<10; i++)
{
var start = new Date();
while((new Date()) - start <= 500) {/*wait*/}
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
}
}
The loop works. The wait works. But the document.getElement is not showing up until the last item in the array...why?
Using setTimeout will allow the code to run and not lock up the page. This will allow it to run the code and will not effect other elements on the page.
var cnt = 0;
(function animateGraph() {
document.getElementById("timeMark").innerHTML = phoneX[cnt].epoch;
cnt++;
if (cnt<10){
window.setTimeout(animateGraph,500);
}
})();
The while loop, waiting for a datetime, is not a good way to wait - it just blocks execution. It keeps the browser (including UI, and its updating) frozen until the script finishes. After that, the window is repainted according to the DOM.
Use window.setTimeout() instead:
function animateGraph(phoneX) {
var el = document.getElementById("timeMark")
var i = 0;
(function nextStep() {
if (i < phoneX.length )
el.innerHTML = phoneX[i].epoch;
i++;
if (i < phoneX.length )
window.setTimeout(nextStep, 500);
})();
}
Please note that this runs asynchronous, i.e. the function animateGraph will return before all phoneXes are shown.
Use setTimeout instead of a while loop.
https://developer.mozilla.org/en/DOM/window.setTimeout
Also try something like this.
Javascript setTimeout function
The following snippet uses a helper function to create the timers. This helper function accepts a loop counter argument i and calls itself at the end of the timer handler for the next iteration.
function animateGraph() {
var graph;
setTimeMarkDelayed(0);
function setTimeMarkDelayed(i) {
setTimeout(function() {
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
if (i < 10) {
setTimeMarkDelayed(++i);
}
}, 3000);
}
}
You actually need some sort of helper function, otherwise you'll end up overwriting the value of i in your for loop in every iteration and by the time your timers run out, i will already be 9 and all handlers will act on the last element in phoneX. By passing i as an argument to the helper function, the value is stored in the local scope of that function and won't get overwritten.
Or you could use setInterval like Radu suggested, both approaches will work.

Using setTimeout to update progress bar when looping over multiple variables

Suppose you have 3 arrays you want to loop over, with lengths x, y, and z, and for each loop, you want to update a progress bar. For example:
function run() {
x = 100;
y = 100;
z = 10;
count = 0;
for (i=0; i<x; i++) {
//some code
for (j=0; j<y; j++) {
// some code
for (k=0; k<z; k++) {
//some code
$("#progressbar").reportprogress(100*++count/(x*y*z));
}
}
}
}
However, in this example, the progress bar doesn't update until the function completes. Therefore, I believe I need to use setTimeout to make the progress bar update while the function runs, although I'm not sure how to do that when you have nested for loops.
Do I need to break each loop up into its own function, or can I leave them as nested for loops?
I created a jsfiddle page in case you'd like to run the current function: http://jsfiddle.net/jrenfree/6V4Xp/
Thanks!
TL;DR: Use CPS: http://jsfiddle.net/christophercurrie/DHqeR/
The problem with the code in the accepted answer (as of Jun 26 '12) is that it creates a queue of timeout events that don't fire until the triple loop has already exited. You're not actually seeing the progress bar update in real-time, but seeing a late report of what the values of the variables were at the time they were captured in the inner closure.
I'd expect that your 'recursive' solution looks a bit like using continuation-passing style to ensure that your loop doesn't continue until after you've yielded control via setTimeout. You might not know you were using CPS, but if you're using setTimeout to implement a loop, you're probably pretty close to it.
I've spelled out this approach for future reference, because it's useful to know, and the resulting demo performs better than the ones presented. With triple nested loops it looks a bit convoluted, so it may be overkill for your use case, but can be useful in other applications.
(function($){
function run() {
var x = 100,
y = 100,
z = 10,
count = 0;
/*
This helper function implements a for loop using CPS. 'c' is
the continuation that the loop runs after completion. Each
'body' function must take a continuation parameter that it
runs after doing its work; failure to run the continuation
will prevent the loop from completing.
*/
function foreach(init, max, body, c) {
doLoop(init);
function doLoop(i) {
if (i < max) {
body(function(){doLoop(i+1);});
}
else {
c();
}
}
}
/*
Note that each loop body has is own continuation parameter (named 'cx',
'cy', and 'cz', for clarity). Each loop passes the continuation of the
outer loop as the termination continuation for the inner loop.
*/
foreach(0, x, function(cx) {
foreach(0, y, function(cy) {
foreach(0, z, function(cz) {
count += 1;
$('#progressbar').reportprogress((100*(count))/(x*y*z));
if (count * 100 % (x*y*z) === 0) {
/*
This is where the magic happens. It yields
control to the javascript event loop, which calls
the "next step of the foreach" continuation after
allowing UI updates. This is only done every 100
iterations because setTimeout can actually take a lot
longer than the specified 1 ms. Tune the iterations
for your specific use case.
*/
setTimeout(cz, 1);
} else {
cz();
}
}, cy);
}, cx);
}, function () {});
}
$('#start').click(run);
})(jQuery);
You can see on jsFiddle that this version updates quite smoothly.
If you want to use setTimeout you could capture the x, y, z and count variables into a closure:
function run() {
var x = 100,
y = 100,
z = 10,
count = 0;
for (var i=0; i<x; i++) {
for (var j=0; j<y; j++) {
for (var k=0; k<z; k++) {
(function(x, y, z, count) {
window.setTimeout(function() {
$('#progressbar').reportprogress((100*count)/(x*y*z));
}, 100);
})(x, y, z, ++count);
}
}
}
}
Live demo.
Probably a jquery function in reportprogress plugin uses a setTimeout. For example if you use setTimeout and make it run after 0 milliseconds it doesn't mean that this will be run immediately. The script will be executed when no other javascript is executed.
Here you can see that i try to log count when its equal to 0. If i do it in setTimeout callback function then that is executed after all cycles and you will get 100000 no 0. This explains why progress-bar shows only 100%. js Fiddle link to this script
function run() {
x = 100;
y = 100;
z = 10;
count = 0;
for (i=0; i<x; i++) {
//some code
for (j=0; j<y; j++) {
// some code
for (k=0; k<z; k++) {
//some code
if(count===0) {
console.log('log emidiatelly ' + count);
setTimeout(function(){
console.log('log delayed ' + count);
},0);
}
count++;
}
}
}
}
console.log('started');
run();
console.log('finished');
wrapping everything after for(i) in setTimeout callback function made the progress-bar work. js Fiddle link
Edit:
Just checked that style setting code for item is actually executed all the time. I think that it might be a browser priority to execute javascript first and then display CSS changes.
I wrote a another example where i replaced first for loop with a setInterval function. It's a bit wrong to use it like this but maybe you can solve this with this hack.
var i=0;
var interval_i = setInterval(function (){
for (j=0; j<y; j++) {
for (k=0; k<z; k++) {
$("#progressbar").reportprogress(100*++count/(x*y*z));
}
}
i++;
if((i<x)===false) {
clearInterval(interval_i);
}
},0);
JS Fiddle
I've found a solution based on the last reply but changing the interval time to one. This solution show a loader while the main thread is doing an intensive task.
Define this function:
loading = function( runme ) {
$('div.loader').show();
var interval = window.setInterval( function() {
runme.call();
$('div.loader').hide();
window.clearInterval(interval);
}, 1 );
};
And call it like this:
loading( function() {
// This take long time...
data.sortColsByLabel(!data.cols.sort.asc);
data.paint(obj);
});

setTimeout in for-loop does not print consecutive values [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I have this script:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
But 3 is alerted both times, instead of 1 then 2.
Is there a way to pass i, without writing the function as a string?
You have to arrange for a distinct copy of "i" to be present for each of the timeout functions.
function doSetTimeout(i) {
setTimeout(function() {
alert(i);
}, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
If you don't do something like this (and there are other variations on this same idea), then each of the timer handler functions will share the same variable "i". When the loop is finished, what's the value of "i"? It's 3! By using an intermediating function, a copy of the value of the variable is made. Since the timeout handler is created in the context of that copy, it has its own private "i" to use.
Edit:
There have been a couple of comments over time in which some confusion was evident over the fact that setting up a few timeouts causes the handlers to all fire at the same time. It's important to understand that the process of setting up the timer — the calls to setTimeout() — take almost no time at all. That is, telling the system, "Please call this function after 1000 milliseconds" will return almost immediately, as the process of installing the timeout request in the timer queue is very fast.
Thus, if a succession of timeout requests is made, as is the case in the code in the OP and in my answer, and the time delay value is the same for each one, then once that amount of time has elapsed all the timer handlers will be called one after another in rapid succession.
If what you need is for the handlers to be called at intervals, you can either use setInterval(), which is called exactly like setTimeout() but which will fire more than once after repeated delays of the requested amount, or instead you can establish the timeouts and multiply the time value by your iteration counter. That is, to modify my example code:
function doScaledTimeout(i) {
setTimeout(function() {
alert(I);
}, i * 5000);
}
(With a 100 millisecond timeout, the effect won't be very obvious, so I bumped the number up to 5000.) The value of i is multiplied by the base delay value, so calling that 5 times in a loop will result in delays of 5 seconds, 10 seconds, 15 seconds, 20 seconds, and 25 seconds.
Update
Here in 2018, there is a simpler alternative. With the new ability to declare variables in scopes more narrow than functions, the original code would work if so modified:
for (let i = 1; i <= 2; i++) {
setTimeout(function() {
alert(i)
}, 100);
}
The let declaration, unlike var, will itself cause there to be a distinct i for each iteration of the loop.
You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
This's Because!
The timeout function
callbacks are all running well after the completion of the loop. In fact,
as timers go, even if it was setTimeout(.., 0) on each iteration, all
those function callbacks would still run strictly after the completion
of the loop, that's why 3 was reflected!
all two of those functions, though they are defined
separately in each loop iteration, are closed over the same shared global
scope, which has, in fact, only one i in it.
the Solution's declaring a single scope for each iteration by using a self-function executed(anonymous one or better IIFE) and having a copy of i in it, like this:
for (var i = 1; i <= 2; i++) {
(function(){
var j = i;
setTimeout(function() { console.log(j) }, 100);
})();
}
the cleaner one would be
for (var i = 1; i <= 2; i++) {
(function(i){
setTimeout(function() { console.log(i) }, 100);
})(i);
}
The use of an IIFE(self-executed function) inside each iteration created a new scope for each
iteration, which gave our timeout function callbacks the opportunity
to close over a new scope for each iteration, one which had a variable
with the right per-iteration value in it for us to access.
The function argument to setTimeout is closing over the loop variable. The loop finishes before the first timeout and displays the current value of i, which is 3.
Because JavaScript variables only have function scope, the solution is to pass the loop variable to a function that sets the timeout. You can declare and call such a function like this:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
You can use the extra arguments to setTimeout to pass parameters to the callback function.
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
Note: This doesn't work on IE9 and below browsers.
ANSWER?
I'm using it for an animation for adding items to a cart - a cart icon floats to the cart area from the product "add" button, when clicked:
function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log('ADDED ONE!');
}, 1000*i);
}
};
NOTE the duration is in unit times n epocs.
So starting at the the click moment, the animations start epoc (of EACH animation) is the product of each one-second-unit multiplied by the number of items.
epoc: https://en.wikipedia.org/wiki/Epoch_(reference_date)
Hope this helps!
You could use bind method
for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
Well, another working solution based on Cody's answer but a little more general can be something like this:
function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}
function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}
yourFunction(timeInSeconds, counter); // well here are the values of your choice.
I had the same problem once this is how I solved it.
Suppose I want 12 delays with an interval of 2 secs
function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}
var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay
the real solution is here, but you need to be familiar with PHP programing language.
you must mix PHP and JAVASCRIPT orders in order to reach to your purpose.
pay attention to this :
<?php
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);
</script>";
}
?>
It exactly does what you want, but be careful about how to make ralation between
PHP variables and JAVASCRIPT ones.

Categories