Проблема в том, что функция обратного вызова происходит через некоторое время после завершения функции ajax. К тому времени индекс массива достигнет конца цикла for
(таким образом, он указывает на конец массива) до неопределенного значения. Массив все еще там, но ваш индекс был изменен к моменту вызова функции завершения.
Техника, обычно используемая для получения индекса в обработчике успеха, заключается в захвате его в закрытии функции, где он захватывается для использования в функции завершения.
Вы можете создать замыкание, которое фиксирует значение индекса, заменив обработчик успеха следующим:
(function(index) {
return function(dataJSON2) {
resultArray = dataJSON2['data'];
resultJSON += friendArray[index] + ":" + resultArray.length + ",";
//alert(resultJSON);
}
}) (i);
Эта внешняя функция выполняется и создает замыкание, которое фиксирует значение i и однозначно делает его доступным для обработчика успеха. Когда он выполняется самостоятельно, он возвращает ваш обработчик успеха, который, таким образом, передается функции getJSON для последующего вызова. Но когда он вызывается позже, нужное вам значение i
становится доступным для обработчика успеха через аргумент в самореализующейся функции.
Вот еще один способ думать о замыканиях, используемых с обратными вызовами.
- Любая функция имеет доступ ко всем переменным, которые находятся в области видимости, когда она объявлена, даже переменным, которые находятся на более высоком уровне в родительских областях, и даже если функция вызывается позже как обратный вызов. На самом деле это огромная особенность javascript, которой нет во многих других языках.
- Итак, если мы хотим, чтобы переменная была доступна для функции обратного вызова позже, когда выполняется обратный вызов, нам просто нужно каким-то образом передать эту переменную в область действия этой функции обратного вызова.
Вот пример этого:
function cycleOnOff(sel, cnt, delay) {
var obj = $(sel);
function next() {
if (cnt-- > 0) {
obj.toggle();
setTimeout(next, delay);
}
}
next();
}
В этом случае функция next()
является обратным вызовом setTimeout()
, но эта функция имеет полный доступ к переменным в пределах родительской области: sel
, cnt
, delay
и obj
.
- Если переменная не изменяется между временем, когда изначально установлен обратный вызов, и когда вызывается обратный вызов, то это довольно просто. Вы можете просто использовать объявление анонимной функции, и все переменные области более высокого уровня, доступные на момент определения анонимной функции, будут по-прежнему доступны при вызове обратного вызова в более позднее время.
- Если переменная действительно изменяется, когда код продолжает выполняться, и вы хотите сделать конкретное значение, которое теперь доступно для обратного вызова, при последующем вызове - это когда она становится немного хитрее. Что можно сделать, так это создать функцию, в которую вы передадите эту переменную, тем самым создав область видимости, в которой эта переменная будет иметь желаемое значение, и она не изменится, так как другой код продолжает выполняться. Но, поскольку нам нужна функция обратного вызова, а не просто вызов функции, мы должны комбинировать их в полу-странном виде. Мы делаем вызов функции и передаем ему желаемое значение. В этом вызове функции мы возвращаем ссылку на нашу функцию обратного вызова. Это назначает функцию обратного вызова соответствующим образом, но она помещает функцию обратного вызова в замыкание, которое фиксирует значение нашей желаемой переменной. Более того, это закрытие является уникальным для этого конкретного экземпляра обратного вызова, поэтому каждое использование обратного вызова будет иметь свое собственное закрытие и, следовательно, свое собственное уникальное значение этой переменной. Закрытие / обратный вызов, который мы создали для вашей конкретной проблемы, является примером этого.