DelayMaker = function() {
this.mkDelay = function(func, funcVars, delay) {
window.setTimeout(function() {
func(funcVars);
},delay);
}
}
Test = function() {
this.init = function() {
var x = 1;
var i;
for(i=0; i < 3; i+=1) {
var DM = new DelayMaker();
DM.mkDelay(function(tmpVars) {
alert(tmpVars);
}, x, 500);
x += 1;
}
}
}
var foo = new Test();
foo.init(); //1, 2, 3
Erklärung: Werden in einer Schleife Timeouts definiert, interessiert das die Schleife nicht. Sie durchläuft mit maximal möglicher Geschwindigkeit, ohne auf das Eintreten der in ihr definierten Timeouts zu warten. Werden innerhalb der in der Schleife definierten anonymen Funktion, welche als Parameter der setTimeout()-Methode definiert ist, Variablen benutzt, die im Scope der Funktion oder ihrer Elternobjekte ist, in welcher
sich die Schleife befindet, so werden die Variablen innerhalb dieses Scopes mit jeder Iteration der Schleife überschrieben. Beim ersten Eintritt eines Timeouts greift JavaScript nun auf den Scope dieser Funktion zu, in
der die Schleife definiert war (trotz eventuellem return-Statement ist der Scope weiterhin vorhanden). Nun ist die Schleife aber i.d.R. schneller abgearbeitet als ein Timeout eintritt, und so greifen letztendlich alle
Timeouts auf den Stand der Variablen nach der letzten Iteration der Schleife zurück!
Die Lösung wie oben gezeigt liegt darin, das der Scope in eine andere Funktion gekapselt wird, die vom Scope der Funktion, welche die iterierende Schleife enthält, unabhängig ist. Diese Hilfsfunktion ruft die als Parameter übergebene anonyme Funktion auf, die selbst wiederum als Parameter eine Variable entgegen nimmt. Diese Variable ist im Idealfall ein Objektliteral. Zum Zeitpunkt des Timeout-Eintritts entspricht der Scope dieser Hilfsfunktion, welche im Prinzip als Methode einer Klasse implementiert ist. Die an ihr übergebenen Variablen werden in ihrem Scope vom Scope der aufrufenden Funktion entkoppelt, archiviert, und zum richtigen Zeitpunkt als
Parameter an die anonyme Funktion übergeben.