-1

I apparently don't fully understand how the setTimeout function works in Javascript:

function move() {
    var a;
    for(a = 0; a < 101; a++){
        setTimeout(function(){
            block.style.marginTop = (750 - a) + 'px');
        }, 1000);
    }
    for(a = 0; a < 101; a++){
        setTimeout(function(){
            block.style.marginTop = (650 + a) + 'px');
        }, 1000);
    }
}

I have tried writing this out in many different ways, but the For Loops always execute instantly every single time. How can I make a For Loop wait for 1 second between each value of 'a'? So, when a = 0, the code executes and then waits for 1 second before running when a = 1, etc. until the first For Loop is finished, then the second For Loop executes in the same way.

Also, is there a more efficient way of doing this than using setTimeout? Like a way of just writing

sleep(1000);

or something like that. This whole setTimeout feature seems very overly complicated if it is the only way of producing delays in javascript. I tried this once but it didn't work at all in any way

await sleep(1000);

Any help with Timeouts and delays in Javascript, especially within a loop, would be greatly appreciated!

  • 4
    Get rid of the loop and use `setInterval` instead. Either way, it doesn't pause the script. That's the important thing to understand. –  May 11 '17 at 20:26
  • There's a bigger problem than the fact that all these setTimeouts will execute at the same time: their callbacks will store a reference to a, not the actual value of `a` at the time you make the setTimeout. This means that, for both for loops, you are calling setTimeout with the value of `a` as 101, 101 times. I suggest you read into something called variable hoisting and start using let/const rather than var. This problem will remain even if, as @swint suggested, you use setInterval. – Douglas Tyler Gordon May 11 '17 at 20:39

3 Answers3

0

You may find value in the answer I posted here. That will explain setTimeout in loops a little more.

Separately, you may want to explain what you are trying to accomplish. It looks like you are either

  • trying to move an element one pixel per second
  • trying to move an element ~100 pixels after one second

For the first option I would use CSS Transitions instead. You'll have a lot more flexibility over how the element moves and you only need to dictate the direction and distance.

For the second option, you could toss the loop and keep the stuff inside, setting the new marginTop to the full value after some timeout.

Community
  • 1
  • 1
ktilcu
  • 3,032
  • 1
  • 17
  • 15
0
    setTimeout(function(){
        block.style.marginTop = (750 - a) + 'px');
    }, 1000);

this part of your code use 'a' variable after 1000 miliseconds. in this time 'a' is 100, because your loop not stoped for run setTimeout function, and it happened because javascript is asyncronous. one solution for solving this problem in js is using recursive functions. if is not necessary to use for loop, you can use this code:

  var a = 0;
  function my_loop(a) {
    if (a < 101) {
      setTimeout(function() {
        block.style.marginTop = (750 - a) + 'px');
        my_loop(a);
      }, 100);

      a++;
    }
  }
  my_loop(a);

but if you want do your question's job, i seriously recommened you to use CSS.

farbod
  • 91
  • 7
0

As @squint mentioned, you can use setInterval for your task.

Here's an example:

// create an element
const width = 10;
const el = document.createElement('div');
el.setAttribute('id', 'main');
document.body.appendChild(el);
el.style.width = width + 'px';

// question-relevant code starts here

const a = [...Array(101).keys()]; // fancy way to create [0, 1, 2, 3, ...]

const it = a[Symbol.iterator](); // for convenience

const int = setInterval(() => {
 const { value, done } = it.next(); // next iteration
 if (done) { clearInterval(int); return }; // finished?
 el.style.width = width + value + 'px'; // adjust width
}, 10);
#main {
  height: 100px;
  width: 10px;
  background: green;
}
Egor Stambakio
  • 17,836
  • 5
  • 33
  • 35