There is an array of items that I work with in a loop. At the end I output console.log (items [a] + ':' + min); min is output normally, items [a] is output as undefined. How to solve the problem with the output items [a]?

var items = [1, 2, 3]; for (var a = 0; a < items.length; a++) { setTimeout(function() { console.log(items[a]); }, 0); } 

Reported as a duplicate at Grundy. javascript Feb 8 '17 at 6:09 pm

A similar question was asked earlier and an answer has already been received. If the answers provided are not exhaustive, please ask a new question .

  • Maybe let's localize the problem? ;) How do you like this option jsfiddle.net/f8r2hgx9/1 ? - Dmitriy Simushev
  • I still do not understand. Can I have another hint? - Alexander Shevtsov
  • And this is not a hint. This is a subtle hint that everything superfluous, not related to the question, should be thrown out of it . - Dmitriy Simushev
  • An example of how to simplify your code in a question, I gave in my answer. - Dmitriy Simushev
  • Inserted your code instead of yours - Alexander Shevtsov

1 answer 1

In fact, your only problem is not understanding the principles of asynchronous code.

In order to simplify the explanation, let me use the following asynchronous function instead of your request function:

 var request = function(callback) { setTimeout(callback, 0); } 

Then, discarding all the code that does not relate to the essence of the question, I get the following test script:

 var request = function (callback) { setTimeout(callback, 0); } var items = [1, 2]; for (var i = 0; i < items.length; i++) { request(function() { console.log(items[i]); }); } 

As you can guess, this code will display undefined three times in the console.

What is the reason?

The reason lies in the asynchronous nature of the request function, which performs a callback on the next turn of the event loop . In fact, this code will be executed in the following sequence:

  1. Initialize i value of 0.
  2. Calling request , which defers the execution of the argument function.
  3. Increasing the loop counter ( i = 1 )
  4. Calling request , which defers the execution of the argument function.
  5. Increasing the loop counter ( i = 2 ) and exiting the loop ( 2 = items.length ).
  6. The execution of the function of paragraph 2
  7. The execution of the function of paragraph 4

And now the most interesting fact: at the time of the actual execution of the function passed in the request , the program execution flow has already reached the end of the cycle and the variable i has the value 2 ! It is quite obvious that items[2] == undefied .

What to do with it?

The traditional method of solving this problem is to create IIFE around the request function with passing a counter variable:

 var items = [1, 2]; for (var i = 0; i < items.length; i++) { (function (counter) { request(function() { console.log(items[counter]); }); })(i); } 

At the same time, the current value of the counter variable is stored in the local scope formed by the closure (through the counter argument) and is no longer dependent on the change in the variable i .

Here is an example on JSFiddle .

A more modern solution is to use let instead of var :

 var items = [1, 2]; for (let i = 0; i < items.length; i++) { request(function() { console.log(items[i]); }); } 

Using let in this case allows you to bind the value of the variable to the local scope of the for loop.

Here is an example on JSFiddle .

  • Many thanks for the detailed answer. Using let instead of var solved my problem completely. - Alexander Shevtsov