JavaScript-асинхронный обратный вызов и область действия

Рассмотрим следующий пример:

var cb = function (t) {
    console.log('callback -->' + t);
};

for(var i = 0; i<3; i++) {
    console.log(i);
    setTimeout(function(){
        cb(i);
    },1000);
}

Рабочий пример в jsfiddle

Вывод этого фрагмента кода:

0
1
2
callback ---> 3
callback ---> 3
callback ---> 3

Все работает так, как ожидалось, поскольку цикл помещает 3 вызова обратного вызова в цикл событий. К концу цикла for я == 3 и когда обратные вызовы выполняются, все они печатают 3, потому что они содержат ссылку на i, которая равна 3. Как можно улучшить этот фрагмент, поэтому, когда обратный вызов будет выполнен, он использует фактическое значение, которое было передано ему.

Выход должен быть:

callback ---> 1
callback ---> 2
callback ---> 3

Спасибо заранее.

Ответ 1

Создайте закрытие, чтобы обработчик setTimeout ссылался на локальную переменную закрытия (которая в этом случае также называлась i), а не i из цикла:

for (var i = 0; i < 3; i++) {
    (function (i) {
        console.log(i);
        setTimeout(function () {
            cb(i);
        }, 1000);
    }(i));
}

Ответ 2

Вы можете попробовать . bind:

for(var i = 0; i<3; i++) {
    console.log(i);
    setTimeout(cb.bind(null, i),1000);
}

Демонстрация

Традиционный способ справиться с этим - создать закрытие:

for(var i = 0; i<3; i++) {
    console.log(i);
    setTimeout((function(i){return function(){cb(i)}}(i)),1000);
}

Ответ 3

Частый вопрос. Попробуем использовать некоторые особенности будущего JS. Я имею в виду let. Он создает локальную переменную области, и вам не нужно использовать закрытие или другой трюк. Но теперь он работает только в FF (я использую 20.0.1)

for(var i = 0; i<3; i++) {
    console.log(i);
    let a = i;
    setTimeout(function(){               
        cb(a);
    },1000);
}

Ответ 4

Я просто использую setTimeout() в вашем функционале cb

var cb = function (t) {
  setTimeout(function(){
    console.log('callback -->' + t);
  },1000);
};


for(var i = 0; i<3; i++) {
    console.log(i);
        cb(i);
}