There is a code in it for() for 2 passes, for each pass an array of data _vars . Inside the loop, I get access to this data _vars[i].param , but in the done() ajax variable i = undefined , a construction like _vars[i] does not plow. How to be?

Theoretically, in the closure, variables are sought in nested functions higher and higher. Is there a function in the callback here and not visible?

Code

 function _init() { _loadingImg.show(); var _dataSend = {}, _vars = [{action:8, name: 'service'}, {action:7, name: 'town'}]; for (var i = 0; i < 2; i++) { _dataSend.action = _vars[i].action; $.ajax({ url: 'query.php', type: 'POST', dataType: 'json', data: _dataSend }) .done(function(response) { var _sel = $('<select></select').attr({ class: 'chosen-select', name: _vars[i].name }); for (var j = 0; j < response.length; j++) { _sel.append('<option value="' + response[j].tid + '">' + response[j].name + '</option>') } $('.box.select').prepend(_sel); }) .fail(function() { console.log("error"); }) .always(function() { $('.chosen-select').chosen(); _loadingImg.hide(); }); } } 

And one more question, in the method of always() there is a disconnection of the loading animation _loadingImg.hide() , how to start it only with the condition of the final execution of both Ajax requests?

  • but exactly i=undefined and not i==2 and because of this _vars[i]==undefined ? - Grundy
  • @Grundy TypeError: _vars [i] is undefined in the line name: _vars [i] .name - Jean-Claude
  • Well, yes :) I’m talking about something - a classic problem with closure and asynchronous function - Grundy
  • now the error is in the question itself: but here in the done () ajax the variable i = undefined and because of it the wrong conclusion was made: is the function apparently visible in the callback and not visible? - Grundy
  • @Grundy, is the loop already completed and the Ajax calls i = 2? - Jean-Claude

1 answer 1

Since javascript is single-threaded, then if an asynchronous operation is called inside a function, for example, ajax request - the processing of its response will be carried out after leaving the main function.

Thus, by the time the done function is executed, the cycle has already completed its work, and the value of the closed variable i is 2.

Standard solution: use IIFE and pass i to it as a parameter

 for (var i = 0; i < 2; i++) { _dataSend.action = _vars[i].action; $.ajax({ url: 'query.php', type: 'POST', dataType: 'json', data: _dataSend }) .done(function(index){ return function(response) { var _sel = $('<select></select').attr({ class: 'chosen-select', name: _vars[index].name }); ... };}(i)); ... 

As for the second question: you need to use the $ .when function

To do this, you can collect Ajax requests into an array and use apply to pass it when .

For example:

 var ajaxs = _vars.map(function(el){ return $.ajax({ url: 'query.php', type: 'POST', dataType: 'json', data: {action: el.action} }).done(...).fail(...); }); $.when.apply($,ajaxs).always(function() { $('.chosen-select').chosen(); _loadingImg.hide(); }); 

In ES2015 you can use spread operator , then the record looks more familiar

 $.when(...ajaxs).always(function() { $('.chosen-select').chosen(); _loadingImg.hide(); }); 

This approach should be used because the when function does not accept an array.

  • OK, thanks, although, of course, I still do not understand the $ .when.apply ($, ajaxs) construction. - Jean-Claude
  • @ Jean-Claude, apply is an analogue of call , except for the method of passing parameters, for call , they are listed in f.call(thisArg, a,b,c,d) , and for apply , by an array f.apply(thisArg,[a,b,c,d]) - Grundy
  • yes yes, it is clear, but in practice they have not yet used them. - Jean-Claude