2013-04-29 2 views
12

Рассмотрим следующий пример:JavaScript асинхронной обратного вызова и область применения

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

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

Working example at jsfiddle

Выходной сигнал этого фрагмента кода является:

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

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

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

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

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

ответ

17

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

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

Большое спасибо. Это просто и работает. –

5

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

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

The demo.

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

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

заметит, что 'bind' является ES5-только –

+0

Большое спасибо за информацию о .bind я буду размышлять над документацией о Это. Bind - это только ES5, поэтому я пока не могу его использовать. Решение о закрытии работает отлично, но Джозеф опубликовал аналогичное решение, поэтому, боюсь, я выберу его ответ. Тем не менее, спасибо за то, что поделились своими знаниями! –

1

Я просто использовать setTimeout() в functoin вашего ЦБ

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


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

Ничего, но нет необходимости в 'cb', чтобы не было определено inline ;-) –

+0

Okey :(@JanDvorak – l2aelba

+0

К сожалению, это невозможно сделать. Обратный вызов вызывается функцией async, но он не содержит одного. –

2

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

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