SetTimeout in a primary for loop

for (var i = 0; i < 4; i++) { setTimeout(function () { console.log(i); }, 1000)} // Output result: 4444Copy the code

Cause: setTimeout is a macro task (executed asynchronously). Each setTimeout task created within the for loop is queued for execution until the end of the for loop. The variable I defined by var is exposed to the global scope. When the for loop ends, I has become 4, and four setTimeouts in the queue output 4444. This one is relatively simple, I believe you understand. ES5 workaround: Use an immediate function to save each I (taking advantage of closures) to print 0123:

for (var i = 0; i < 4; i++) { (function (a) { setTimeout(function () { console.log(a); }, 1000); }(i)); // for (var I = 0; i < 4; i++) { setTimeout(fn(i), 1000); function fn (a) { return function () { console.log(a); }}} // The output is 0123Copy the code

ES6 workaround: Using let declaration I, let solves the memory leak, global pollution and other problems of var declaration. The LET declaration will be block-level scoped within the for loop, and the I of each for loop will be valid only in this round. The code is as follows:

for (let i = 0; i < 4; i++) { setTimeout(function () { console.log(i); }, 1000)} // The output is 0123Copy the code

SetTimeout in the for loop

for (var i = 0; i < 4; i++) { var timer = setTimeout(function(i) { console.log(i); clearTimeout(timer) }, 1000, i); } // Output: 012Copy the code

Small ❔, do you have many small ðŸ‘ķ~~~~ first setTimeout parameter a random circle ðŸ˜ą, why three parameters? ! In simple terms, a setTimeout can take several parameters, and any parameters other than function and delay are passed to function at the end of the delay. Like:

setTimeout(function(msg1,msg2,...) {},1000,' callback parameter 1',' callback parameter 2',...) ;Copy the code

If you do not understand setTimeout, click here, if you already know the first to give yourself a clap 😉 continue to see the program, see the program execution result [0 1 2], is a confused circle ðŸĪŠ why less [3], who am I, where am I? ðŸĪŊ actually works like this, passing I from the third argument of setTimeout into function, forming a closure, so you can print [0, 1, 2] instead of [4, 4, 4]. The reason why there is no output [3] is that the timer does not form a closure. Each for loop creates a timer, and the execution of setTimeout needs to wait for the end of the for loop. When the for loop ends, the timer has become the fourth timer (if you want to verify that the timer was the last timer, you can print the timer in function), so clearTime only clears the last timer, so [3] is gone. Some people might think ðŸĪ“, timer also becomes a parameter, pass, form a closure to solve the problem, the code is as follows:

for (var i = 0; i < 4; i++) { var timer = setTimeout(function(i, timer) { console.log(i); clearTimeout(timer) }, 1000, i, timer); } // The output is 0123Copy the code

It looks like it solves the problem, but actually there is a problem. I’m going to change the code a little bit, and setTimeout prints timer internally,console.log(i);Instead ofConsole. log(' I: '+ I, "timer: "+ timer);The running result is shown as follows:



There are two reasons for this problem: 👉 First, the var definition variable has variable promotion, its value is undefined; The second is that the assignment operator executes from right to left.

The execution of the above code: When executing the first loop of the for loop, setTimeout is executed first because the assignment expression is executed from right to left. When transmitting the value of timer, no error is reported because of variable promotion, but the value is undefined. After that, setTimeout generates the id and assigns the value to the timer. When the second for loop is executed, setTimeout is executed first, and the timer passed is the value of the previous timer, and so on until the end of the for loop, which results in the last delay not being cleared.

The solution: Of course, you can use the ES6 let to define the timer. The principle is the same as the let introduced above. The code is as follows:

for (var i = 0; i < 4; i++) { let timer = setTimeout(function(i) { console.log(i); clearTimeout(timer) }, 1000, i); } // The output is 0123Copy the code

SetInterval (not clear timer)

for (var i = 0; i < 4; i++) { setInterval(function () { console.log(i); }, 1000)} // Output result: 44444444444444.... (Infinite loop)Copy the code

Cause: setInterval and setTimeout are similar in that they are macro tasks (executed asynchronously). Each setInterval task created within a for loop is queued up for execution until the for loop ends. The variable I defined by var is exposed to the global scope, and when the for loop ends and I becomes 4, four more setintervals in the queue will print 4444 (infinite times). This one is relatively simple, I believe you understand. ES5 workaround: Use an immediate function to save each I (taking advantage of closures) to print 0123 (infinite times) as follows:

for (var i = 0; i < 4; i++) { (function (a) { setInterval(function () { console.log(a); }, 1000); }(i)); // Pass I as an argument to a} // Output: 012301230123... // For (var I = 0; i < 4; i++) { setInterval(fn(i), 1000); function fn (a) { return function () { console.log(a); }}} // Output result: 012301230123... (Infinite repetition)Copy the code

ES6 workaround: Using let declaration I, let solves the memory leak, global pollution and other problems of var declaration. The LET declaration will be block-level scoped within the for loop, and the I of each for loop will be valid only in this round. The code is as follows:

for (let i = 0; i < 4; i++) { setInterval(function () { console.log(i); }, 1000)} // Output result: 012301230123... (Infinite repetition)Copy the code

SetInterval (Clear timer) for loop

for (var i = 0; i < 4; i++) { var timer = setInterval(function(i) { console.log(i); clearInterval(timer) }, 1000, i); } // Output result: 012012012... (Infinite loop)Copy the code

This is exactly the same reason for setTimeout as before, including parameter passing, as follows:

setInterval(function(msg1,msg2,...) {},1000,' callback parameter 1',' callback parameter 2',...) ;Copy the code

Also, the following closure idea should not be used to solve the problem, because it will result in the last timer not being cleared, resulting in the output of the last I, code as follows:

for (var i = 0; i < 4; I ++) {var timer = setInterval(function (I, timer) {console.log(' I: '+ I, "timer: "+ timer); clearInterval(timer) }, 1000, i, timer); }Copy the code

The results are as follows:



The reason is exactly the same as setTimeout explained before, except that setInterval will be executed continuously, so it will continuously output the last result.

The solution: Of course, you can use the ES6 let to define the timer. The principle is the same as the let introduced above. The code is as follows:

for (var i = 0; i < 4; I ++) {let timer = setInterval(function (I) {console.log(' I: '+ I, "timer: "+ timer); clearInterval(timer) }, 1000, i); }Copy the code

The results are as follows:

Have not understand, can 👉 see here

If it is helpful to you, you can pay attention to 👍+, we learn front-end 😁 together.