2011-10-13 6 views
24

Sto tentando di eseguire più timer dato un elenco variabile di elementi. Il codice simile a questa:Come utilizzare la funzione setInterval all'interno del ciclo

var list = Array(...); 

for(var x in list){ 
    setInterval(function(){ 
     list[x] += 10; 
     console.log(x + "=>" + list[x] + "\n"); 
    }, 5 * 1000); 
} 

Il problema con il codice di cui sopra è che l'unico valore in corso di aggiornamento è l'elemento alla fine della lista, moltiplicato per il numero di elementi nella lista.

Qualcuno può offrire una soluzione e qualche spiegazione, quindi so perché si comporta in questo modo?

risposta

32

Così, un paio di cose:

  1. più importante, la funzione di callback che hai passato a setInterval() mantiene un riferimento alla x piuttosto che il valore un'istantanea della x come esisteva nel corso di ogni particolare iterazione. Pertanto, poiché x viene modificato nel ciclo, viene aggiornato anche in ciascuna delle funzioni di callback.
  2. Inoltre, for...in viene utilizzato per enumerare le proprietà dell'oggetto e può essere behave unexpectedly quando utilizzato su matrici.
  3. Inoltre, ho il sospetto che vogliate davvero lo setTimeout() anziché lo setInterval().

È possibile passare argomenti alla funzione di callback fornendo ulteriori argomenti a setTimout():

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

Ecco un esempio:

var list = [1,2,3,4]; 
 

 
for (var x = 0, ln = list.length; x < ln; x++) { 
 
    setTimeout(function(y) {  
 
    console.log("%d => %d", y, list[y] += 10); 
 
    }, x * 500, x); // we're passing x 
 
}

Per fortuna, i numeri viene passato dal valore piuttosto che dal riferimento.

+0

Sì, potrei consigliare di leggere questo articolo: http://blog.morrisjohns.com/javascript_closures_for_dummies. Inoltre, setTimeout all'interno di un ciclo potrebbe non essere ciò che vuole fare in quanto tutte le richiamate verranno attivate nello stesso momento, non verranno sfalsate. – SoWeLie

+0

L'OP non ha "bisogno di una chiusura", al contrario - ha una chiusura a * x * che deve essere evitata (cosa che la tua risposta effettivamente fa). Mantiene comunque la chiusura in * elenco *. – RobG

33

Ecco il codice di lavoro:

var list = [1, 2, 3, 4, 5]; 

for (var i = 0, len = list.length; i < len; i += 1) { 
    (function(i) { 
     setInterval(function() { 
      list[i] += 10; 
      console.log(i + "=>" + list[i] + "\n"); 
     }, 5000) 
    })(i); 
} 

Qui l'indice i è memorizzato in una funzione anonima in modo che non venga sovrascritta da cicli consecutivi. La funzione setInterval nel codice mantiene il riferimento solo all'ultimo valore di i.

+0

buona soluzione !! –

+0

Ottima soluzione! Una piccola nota: tutto ciò che può essere modificato nel ciclo può essere passato alla funzione anonima, non solo all'indice. –

2

Non è necessario utilizzare un ciclo for con l'istruzione setInterval. Prova questo:

var list = Array(...); 
var x = 0; 

setInterval(function() { 

    if (x < list.length;) { 
     list[x] += 10; 
     console.log(x+"=>"+list[x]); 
    } 

    else return; 

    x++; 
}, 5000); 
0

Se si dispone di serie JSON e jQuery incluso, è possibile utilizzare:

$.each(jsonArray, function(i, obj) { 
    setInterval(function() { 
     console.log(i+' '+obj); 
    }, 10); 
}); 
2

non so come fare questo con un ciclo, ma questo codice qui stamperà ciascun elemento di un array ad intervalli cronometrati:

function displayText(str) { 
    $('.demo').append($('<div>').text(str)); 
} 
var i = 0; 

var a = [12, 3, 45, 6, 7, 10]; 

function timedLoop() { 
setTimeout(function() { 
    displayText(a[i]); 
    i++; 
    if(i < a.length) { 
     timedLoop(); 
    } 
}, 2000) 
} 

timedLoop(); 

Usando un po 'di jquery per vederlo nel browser.

Problemi correlati