2013-04-29 14 views
12

Rozważmy następujący przykład:JavaScript asynchroniczny callback i zakres

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

wyjściu tego fragmentu kodu jest:

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

wszystko działa zgodnie z oczekiwaniami, na pętli stawia 3 zwrotnego wywołuje pętlę zdarzeń. Pod koniec pętli for i == 3 i po wykonaniu wywołań wszystkie z nich wypisują 3, ponieważ zawierają łącze do i, które jest 3. W jaki sposób można poprawić ten fragment, więc gdy wywołanie zwrotne zostanie użyte, używa rzeczywistą wartość, która została mu przekazana.

Wyjście powinno być:

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

góry dzięki.

Odpowiedz

17

Create a closure tak, że handler setTimeout będzie odnosić się do zmiennej zamknięcia na lokalnym (który w tym przypadku, również o nazwie i), a nie i z pętli:

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

Wielkie dzięki. To proste i działa. –

5

Można spróbować .bind:

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

The demo.

Tradycyjny sposób obsłużyć to jest stworzenie Zamknięcie:

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

uwaga, że ​​'bind' jest ES5-tylko –

+0

dziękuję za info na .bind będę rozważać nad dokumentacją na temat to. Bind to tylko ES5, więc nie mogę go jeszcze użyć. Twoje rozwiązanie zamknięcia działa dobrze, jednak Joseph opublikował podobne rozwiązanie, więc obawiam się, że wybiorę jego odpowiedź. Niemniej dziękuję za podzielenie się swoją wiedzą! –

1

będę po prostu użyć setTimeout() w functoin Twojego CB

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


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

Przyjemnie, ale nie ma potrzeby, aby 'cb' nie był zdefiniowany inline ;-) –

+0

Okey :(@JanDvorak – l2aelba

+0

Niestety nie można tego zrobić Callback jest wywoływana przez funkcję asynchroniczną, ale nie zawiera ona samej siebie. –

2

Częste pytanie. Spróbujmy wykorzystać niektóre cechy przyszłości JS. Mam na myśli let. Tworzy lokalną zmienną zasięgu i nie trzeba używać zamknięcia ani innej sztuczki. Ale teraz to działa tylko w FF (używam 20.0.1)

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