2012-02-28 20 views
7

Estoy luchando con el siguiente código. He intentado muchas formas diferentes de hacerlo, pero termino con uno de los dos resultados incorrectos.Javascript - problema de cierre de setTimeout

for(i = 0; i < result.length; i++) { 

    var tmpBlockInfo = { 
     size: worldTest.data[0].size, 
     xStartPixel : result[i].x * worldTest.data[0].size, 
     yStartPixel : result[i].y * worldTest.data[0].size, 
     blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
    } 

    var tmpFunc = function(){ 
     worldTest.fillBlock(tmpBlockInfo, 157, 152, 124, 255) 
    }; 

    var t = setTimeout(function(){ 
     tmpFunc() 
    } , 500 * i); 
} 

El problema con el código anterior es tmpBlockInfo siempre recibe el último resultado [i] .x/resultado [i] .y. Así que estoy esperando cuando el tiempo de espera ejecuta la función es ver qué resultado [i] .x/result [i] .y se dejó después del bucle (en lugar de pasarlo como una "nueva" variable)

I Pensar en ponerlo en una función solucionaría el problema del cierre, pero no hay suerte.

También probamos:

for(i = 0; i < result.length; i++) { 

    var tmpBlockInfo = { 
     size: worldTest.data[0].size, 
     xStartPixel : result[i].x * worldTest.data[0].size, 
     yStartPixel : result[i].y * worldTest.data[0].size, 
     blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
    } 

    var t = setTimeout(function(){ 
     worldTest.fillBlock(tmpBlockInfo, 157, 152, 124, 255) 
    } , 10000 * i); 
} 

Con los mismos resultados que el primer código.

Si hago:

for(i=0; i < result.length; i++) { 

    var tmpBlockInfo = { 
     size: worldTest.data[0].size, 
     xStartPixel : result[i].x * worldTest.data[0].size, 
     yStartPixel : result[i].y * worldTest.data[0].size, 
     blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
    } 

    setTimeout(function(passBlockInfo) { 
     worldTest.fillBlock(tmpBlockInfo, 157, 152, 124, 255) 
    } (tmpBlockInfo), 1000 * i); 
} 

lo hace procesar todas las funciones fillBlock correctamente. PERO los hace todos al mismo tiempo (por ejemplo, no los dispara uno a la vez. Simplemente los hace uno tras otro pero provoca el bloqueo (sin actualización de la pantalla) y sin demora entre cada uno.

Ayuda con esto sería genial!

+0

Aunque esta es una pregunta común, +1 para mostrar varios buenos intentos de resolver el problema. –

+0

...su último intento está muy cerca, pero necesita * devolver * una función que ejecute su código para que 'setTimeout' pueda ejecutarlo más tarde, y necesita usar el parámetro' passBlockInfo' en lugar del 'tmpBlockInfo' –

Respuesta

7

la razón es que ellos se está ejecutando de inmediato se debe a que se está ejecutando la función de la llamada setTimeout. lo que me gustaría hacer es crear otra función como tal

function MakeTimeoutCall(fn, data, timeout){ 
    setTimeout(function() {fn.call(null, data);}, timeout); 
} 

Luego, en su bucle donde llama a setTimeout, haga esto

MakeTimeoutCall(
    function(passBlockInfo){ 
     worldTest.fillBlock(passBlockInfo, 157, 152, 124, 255); 
    }, 
    tmpBlockInfo, 
    1000 * i); 

(Asumiendo que worldTest es un objeto global).

Eso debería funcionar. setTimeout espera que una función llame al final del tiempo de espera, le está dando su función, pero la llama de inmediato. El valor de retorno, en este caso nulo, se otorga al tiempo de espera. Entonces no hay tiempo de espera, todo sucede a la vez.

Por si acaso mi respuesta es un poco complicada dado el contexto, here es un enlace a una solución más simple en jsfiddle.

+0

P.S. Asegúrese de ubicar su variable i con var, de lo contrario se considerará global y no hay nada más peligroso que una variable de bucle global para causar errores difíciles de depurar. – Zoidberg

+0

Hola, gracias por la respuesta! Acabo de probar esto, pero estoy obteniendo el mismo problema que los ejemplos del código 1 y 2 anteriores. Por lo que puedo decir, ¿tu punto aquí es solo otra forma de hacer lo que hice en los ejemplos de código 1 y 2? – james

+0

Olvidé el * i en mi tiempo de espera, déjame arreglar eso. – Zoidberg

4

Prueba esto:

for(i = 0; i < result.length; i++) { 
    var tmpBlockInfo = { 
     size: worldTest.data[0].size, 
     xStartPixel : result[i].x * worldTest.data[0].size, 
     yStartPixel : result[i].y * worldTest.data[0].size, 
     blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
    } 
    var t = setTimeout(createFillBlockFn(tmpBlockInfo) , 500 * i); 
} 

function createFillBlockFn(blockInfo) { 
    return function() { 
     worldTest.fillBlock(blockInfo, 157, 152, 124, 255) 
    } 
} 
+0

+1 Creo que este es un enfoque más limpio. –

+0

Este es un buen enfoque. El único inconveniente es que JSLint se quejará de ello, pero una vez más, a JSLint solo le gusta herir los sentimientos de la gente. – Zoidberg

+0

@amnotiam: ¿El valor de 'i' no siempre será el mismo dentro del cierre? Eso no parece ocuparse de ese infame problema de cierre. – Sarfraz

0

se necesita una función de envoltura para retener alcance. Esta es solo una de las pocas maneras de hacerlo.

for(i=0; i < result.length; i++) { 

    setTimeout(function() { 

     var tmpBlockInfo = { 
      size: worldTest.data[0].size, 
      xStartPixel : result[i].x * worldTest.data[0].size, 
      yStartPixel : result[i].y * worldTest.data[0].size, 
      blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
     }; 

     return function() { 
      worldTest.fillBlock(tmpBlockInfo, 157, 152, 124, 255); 
     }; 
    }(), 1000 * i); 
} 
0

Este código le ayudará a:

for(i=0; i < result.length; i++) 
{ 
    setTimeout(function(){ 
     { 
      size: worldTest.data[0].size, 
      xStartPixel : result[i].x * worldTest.data[0].size, 
      yStartPixel : result[i].y * worldTest.data[0].size, 
      blockType : (Math.random() * 100 > 10) ? 'path' : 'wall' 
     }, 157, 152, 124, 255); 
    } , 500 * i); 
} 

Las variables se declaran en la parte superior de la función, por lo que tiene una var tmpBlockInfo, ONT tmpFunc, y aquellas variables que reasignar.

Cuestiones relacionadas