setTimeout with Loop in JavaScript

后端 未结 9 1579
挽巷
挽巷 2021-01-05 16:08

I have a very trivial question. For a simple loop with setTimeout, like this:

for (var count = 0; count < 3; count++) {
    setTimeout(function() {
               


        
相关标签:
9条回答
  • 2021-01-05 16:08

    This has to do with how scoping and hoisting is being treated in JavaScript.

    What happens in your code is that the JS engine modifies your code to this:

    var count;
    
    for (count = 0; count < 3; count++) {
        setTimeout(function() {
            alert("Count = " + count);
        }, 1000 * count);
    }
    

    And when setTimeout() is being run it will first look in it's own scope after count but it won't find it so then it'll start looking in the functions that closes (this is called closures) over the setTimeout function until it finds the var count statement, which will have the value 3 since loop will have finished before the first timeout function has been executed.

    More code-ily explained your code actually looks like this:

    //first iteration
    var count = 0; //this is 1 because of count++ in your for loop.
    
    for (count = 0; count < 3; count++) { 
        setTimeout(function() {
            alert("Count = " + 1);
        }, 1000 * 1);
    }
    count = count + 1; //count = 1
    
    //second iteration
    var count = 1;
    
    for (count = 0; count < 3; count++) {
        setTimeout(function() {
            alert("Count = " + 2);
        }, 1000 * 2);
    }
    count = count + 1; //count = 2
    
    //third iteration
    var count = 2;
    
    for (count = 0; count < 3; count++) {
        setTimeout(function() {
            alert("Count = " + 3);
        }, 1000 * 3);
    }
    count = count + 1; //count = 3
    
    //after 1000 ms
    window.setTimeout(alert(count));
    //after 2000 ms
    window.setTimeout(alert(count));
    //after 3000 ms
    window.setTimeout(alert(count));
    
    0 讨论(0)
  • 2021-01-05 16:08

    think about it like that:

    AFTER the 1000*n miliseconds are over, what will be the value of count?

    of course it will be 3, because the foor loop ended way earlier than the timeout of 1000*n ms.

    in order to print 1,2,3 you'll need the following:

    for (var count = 0; count < 3; count++) {
        do_alert(num);
    }
    
    function do_alert(num) {
        setTimeout(function() {
            alert("Count = " + num);
        }, 1000 * num);
    }
    

    a different approach is to make it a closure function (explained well in JavaScript closures vs. anonymous functions)

    for (var count = 0; count < 3; count++) {
        (function(num){setTimeout(function() {
            alert("Count = " + num);
        }, 1000 * num)})(count);
    }
    

    these two code samples will actually work similarly.

    the first sample calls a named function (do_alert) each iteration.

    the second sample calls a CLOSURE anonymous function (which is just like do_alert) each iteration.

    it's all a matter of SCOPE.

    hope that helps.

    0 讨论(0)
  • 2021-01-05 16:12

    That is because by the time the for loop completes its execution the count is 3, and then the set timeout is called.

    Try this:

    var count = 0; 
    setTimeout(function() {
           for (count = 0; count < 3; count++) {
               alert("Count = " + count);
            }
    }, 1000* count);
    
    0 讨论(0)
  • 2021-01-05 16:14

    That's because all the timeouts are run when the loop finished.

    The timeout functions then take the current value of count.

    And thats always 3 because the for loop has finished.

    0 讨论(0)
  • 2021-01-05 16:15

    Easy fix here is to utilize es6 let local variable. Your code will look almost the same except it will do what you expect :)

     for (let count = 0; count < 3; count++) {
        setTimeout(function() {
            alert("Count = " + count);
        }, 1000 * count);
    
    }
    

    Or you could create a recursive function to get that job done, as following:

    function timedAlert(n) {
      if (n < 3) {
        setTimeout(function() {
            alert("Count = " + n);
            timedAlert(++n);
        }, 1000);
      }
    }
    
    timedAlert(0);
    
    0 讨论(0)
  • 2021-01-05 16:21

    First, setTimeout(function, milliseconds) is a function which takes a function to execute after "milliseconds" milliseconds.

    Remember, JS treats functions as objects, so the for(...) loop will initially produce something like:

    setTimeout( ... ) setTimeout( ... ) setTimeout( ... )

    Now the setTimeout() functions will execute one by one.

    The setTimeout() function will try to find the count variable in the current scope. Failing that, it will go to the outer scope and will find count, whose value is already incremented to 3 by the for loop.

    Now, starting execution....The first alert shows immediately, as the milliseconds is 0, the second alert shows after 1000 ms, and then the third alert shows after 2000 ms. All of them shows Count = 3

    0 讨论(0)
提交回复
热议问题