There is a setTimeout inside the for loop:

 for (var i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); } 

I want to show the numbers 1 , 2 , 3 , 4 , 5 , but it shows 6 , 6 , 6 , 6 , 6 . Why?

2 answers 2

The point is that the function is executed after the cycle ends. Therefore, i is equal to 6 when console.log(i) is executed for the first time.

If it is still not clear, here is a similar example in pseudocode:

 Π£ мСня 1 камСнь. Π§Π΅Ρ€Π΅Π· ΠΌΠΈΠ½ΡƒΡ‚Ρƒ скаТи, сколько Ρƒ мСня ΠΊΠ°ΠΌΠ½Π΅ΠΉ. Π”Π°ΠΉ ΠΌΠ½Π΅ камСнь сСйчас. Π§Π΅Ρ€Π΅Π· 2 ΠΌΠΈΠ½ΡƒΡ‚Ρ‹ скаТи, сколько Ρƒ мСня ΠΊΠ°ΠΌΠ½Π΅ΠΉ. Π”Π°ΠΉ ΠΌΠ½Π΅ камСнь сСйчас. 

It turns out that now I will give 2 stones, in the end I will have 3. In a minute, he will say how many stones I have (ie, 3), and in two minutes he will say again that I have 3 stones.

How to solve it?

  1. Insert an asynchronous function into an anonymous function to transfer a different value i to the output function at each iteration. This is the most common solution.

 for(var i = 1; i <= 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i); } 

Creating many of the same functions is not very good, you can raise this function above:

 function pass(i) { return function () { console.log(i); } } for(var i = 1; i <= 5; i++) { setTimeout(pass(i), i * 1000); } 

  1. Use recursion instead of looping. This is also a common decision.

 (function f(i) { if (i > 5) return; setTimeout(function() { console.log(i); f(i + 1); }, 1000); })(1); 

  1. Use Function.prototype.bind() to create a new function at each iteration. This is shorter than the first option, but IE8 and below do not support .bind .

 for (var i = 1; i <= 5; i++) { setTimeout(function(i) { console.log(i); }.bind(null, i), i * 1000); } 

  1. Pass arguments with setTimeout . This is supported by all modern browsers, but if we are talking about old ones, then it is worth checking out.

 for(var i = 1; i <= 5; i++) { setTimeout(function (i) { console.log(i); }, i * 1000, i); } 

I note that now the functions in the loop are no different from each other, so you can make one function:

 function doSmth(i) { console.log(i); } for(var i = 1; i <= 5; i++) { setTimeout(doSmth, i * 1000, i); } 

  1. Use let . This is a convenient option, but it is a new feature in ECMAScript 2015, so it still does not work in most browsers. If you want to use ECMAScript 2015 to the point that browsers support it, I recommend trying Babel .

    Warning: some browsers (for example, IE 11) support let , but do not support it in the for loop.

 for (let i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); } 

  • You can still (as a solution to the problem) use setInterval . - Regent
  • @Regent I did not understand what you mean by calling a function with i + 1 in setTimeout . Could you explain? And yes, in this case the setInterval option is convenient, but I wanted an answer that works in the general case. Maybe instead of setTimeout , there is an AJAX request or another asynchronous function. - Peter Olson
  • This is because I could not clearly explain. In general, I’m talking about this option and about the fact that it allows you to change the delay time or prematurely interrupt the β€œcycle” in case of any changes. At the same time, it is not so much more difficult / longer. - Regent
  • So the options offered by you will not wait for the end of the Ajax request either. Therefore, the option with setInterval will work exactly the same. - Regent
  • @Regent So, I understand, thanks. Added in response. - Peter Olson

Async solution

 const intArray = [1, 2, 3, 4, 5]; async.eachSeries(intArray, function(i, callback) { setTimeout(function() { console.log(i); callback(); }, i * 1000); }, function(err) { // if any of the file processing produced an error, err would equal that error if( err ) { console.log('An error occured. All processing has stopped'); } else { console.log('Whole array processed successfully'); } }); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/async/2.4.0/async.js"></script>