1

I'm trying to make a few things scroll down the screen in javascript, however, upon execution, it just says a little and displays everything at once. So it's not clearing with the $("#Menu").html('') function and the setTimeout(function {},500) is just setting a timeout for the entire page instead of the code segment.

var MenuData = [
{'Name':'pictures','x':'30'},
{'Name':'blog','x':'50'},
{'Name':'contact','x':'42'}
]
;  


var PositionArray = new Array();
$(document).ready(function () {
    for (var count = 0; count < 1000; count++) {
        $("#Menu").html('');
        if (PositionArray[count] != null) {
            PositionArray[count]++;
        } else {
            PositionArray[count] = 0;
        }

        setTimeout(function () {
        for (var i in MenuData) {
                $("#Menu").append('<div style="position:relative; left:' + MenuData[i].x + 'px; top:' + PositionArray[i] + 'px; ">123</div>');
            }
        }, 500);

    }
});

Here's the fiddle: http://jsfiddle.net/LbjUP/

Edit: There was a little bit of error in the code that doesn't apply to the question. Here's the new one: http://jsfiddle.net/LbjUP/1/, I just moved PositionArray[count] to the setTimeout function as PositionArray[i]

JVE999
  • 3,327
  • 10
  • 54
  • 89
  • 1
    Because you're giving them all the same duration. Note, you also have serious scoping problems due to using a setTimeout in a for loop without properly creating a child scope that preserves the value of `i` – Kevin B Sep 05 '13 at 22:28
  • 1
    Basically, you're instantly creating 1000 settimeouts all at the same time, that will all trigger after 500 ms. therefore, after 500ms, all 1000 will trigger. – Kevin B Sep 05 '13 at 22:29
  • 1
    You could use 500*count I suppose. You could also set it up so the setTimeout function recursively calls itself. – PherricOxide Sep 05 '13 at 22:30
  • Is there a way to have setTimeout() call every time rather, than waiting until the end of the for loop (which doesn't make sense to me -- why would it do that?)? – JVE999 Sep 05 '13 at 22:31
  • The outer Loop will be processed much more quickly than the 500 ms Delay, so most of the timeouts, probably all of them, Will have been set before the first of them Executes. Also the difference Between Start Times of any 2 consecutively Registered timeouts will be miniscule. – collapsar Sep 05 '13 at 22:32
  • @KevinB: The loop over `i` is *inside* the scheduled function, there are no issues with scope. The loop over `count` could raise some, but he doesn't use that variable inside the closure. – Bergi Sep 05 '13 at 22:33
  • setTimeout() **is** called every time, it is the code inside that waits. However your loop runs so quickly that they are all started at basically the same time, then they all wait the same 500ms, then run all at once. Does that make sense? – Jordan Sep 05 '13 at 22:35
  • I see now, how would I get it to pause after the setTimeout loop? – JVE999 Sep 05 '13 at 22:36
  • a viable approach might be to convert the loop to tail recursion. Wrap the code into a function, call setTimeout as the last statement in this function calling itself with 500ms delay and a preincremented counter. Skip setTimeout if the counter is greater than a threshold (1000). – collapsar Sep 05 '13 at 22:37
  • @collapsar If I'm going to add a delay, couldn't I just add a delay here? Or should I use a setInterval and clear the interval after a certain count, which seems a little odd, but might work? – JVE999 Sep 05 '13 at 22:41
  • Btw, don't use [`for in`-loops on that `MenuData` array](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea) – Bergi Sep 05 '13 at 22:49
  • 1
    @Jamil: See my answer. `setInterval` should work too, if you clear the interval timer after 1000 calls. Should in fact work better as you avoid the call Stack. – collapsar Sep 05 '13 at 22:50

3 Answers3

2

As stated in the comments, you are creating 1000 timeouts for 500 ms at the same time - after 500 ms all of them will be executed. What you want is to increase the timeout for every scheduled function:

setTimeout(function() {
    // do something
}, count * 500);

However, creating 1000 timeouts at once is not a that good idea. It would be better to use setInterval or call setTimeout "recursively" until a count of 1000 is reached, so that you only have one active timeout at a time.

var count = 0;
function update() {
    // do something
    if (++count < 1000)
        setTimeout(update, 500);
    // else everything is done
}
update();

Also, if you intend to create timeouts in a loop, be sure to be familiar with closures and their behavior when accessing counter variables after the loop ran.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Setting `setTimeout` recursively is what I'd like to do. That would require pausing after each setTimeout or something that does the same thing. How would I do that? I was under the impression setTimeout pauses the code, procedurally, which apparently it doesn't. – JVE999 Sep 05 '13 at 22:39
  • No. `setTimeout` does *asynchronically* schedule the execution of the given function for the specified time, and returns immediately. It is not a `sleep` function, which would freeze the whole browser UI. – Bergi Sep 05 '13 at 22:43
  • Alright. I tried the first method and it did pretty much the same thing, although it seemed to delay or something a bit more: http://jsfiddle.net/LbjUP/2/ I've done functions similar to the second, so it should work, I'm going to try it out. – JVE999 Sep 05 '13 at 22:46
0

Try

function recurse ( cnt ) {
    for (var i in MenuData) {
        $("#Menu").append('<div style="position:relative; left:' + MenuData[i].x + 'px; top:' + PositionArray[i] + 'px; ">123</div>');
    }
    if (cnt < 1000){
       setTimeout(function () { recurse(cnt + 1); }, 500);
    }
}

$("#Menu").html('');
if (PositionArray[count] != null) {
    PositionArray[count]++;
} else {
    PositionArray[count] = 0;
}
recurse(0);
collapsar
  • 17,010
  • 4
  • 35
  • 61
0

You can also use setInterval

let i = 0;
const interval = setInterval(() => {
    console.log(i);
    i++;
    if (i >= 10) {
        clearInterval(interval);
    }
}, 1000);`