На самом деле, единственная ваша проблема заключается в непонимании принципов работы асинхронного кода.
Для того, чтобы упростить объяснение, позволю себе использовать вместо вашей функции request вот такую асинхронную функцию:
var request = function(callback) {
setTimeout(callback, 0);
}
Тогда, отбросив весь тот код, что не относится к сути вопроса, я получаю вот такой тестовый сценарий:
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]);
});
}
Как вы можете догадаться, этот код три раза выведет undefined в консоль.
В чем причина?
Причина кроется в асинхронной природе функции request, которая выполняет callback на следующем витке event loop. По-факту, этот код будет выполняться вот в такой последовательности:
- Инициализация
i значением 0.
- Вызов
request, которая откладывает выполнение функции-аргумента.
- Увеличение счетчика цикла (
i = 1)
- Вызов
request, которая откладывает выполнение функции-аргумента.
- Увеличение счетчика цикла (
i = 2) и выход из цикла (2 = items.length).
- Выполнение функции из п.2
- Выполнение функции из п.4
А теперь самый интересный факт: на момент реального выполнения функции, переданной в request, поток выполнения программы уже дошел до конца цикла и переменная i имеет значение 2! Вполне очевидно, что items[2] == undefied.
Что с этим делать?
Традиционным методом решения этой проблемы является создание IIFE вокруг функции request с передачей переменной-счетчика:
var items = [1, 2];
for (var i = 0; i < items.length; i++) {
(function (counter) {
request(function() {
console.log(items[counter]);
});
})(i);
}
При этом, текущее значение переменной-счетчика сохраняется в локальной области видимости, образованной замыканием (через аргумент counter) и уже не зависит изменения переменной i.
А вот и пример на JSFiddle.
Более современным способом решения, является использования let вместо var:
var items = [1, 2];
for (let i = 0; i < items.length; i++) {
request(function() {
console.log(items[i]);
});
}
Использование let в данном случае позволяет привязать значение переменной к локальной области видимости цикла for.
А вот и пример на JSFiddle.