Alters

Timer.js: Desarrollo del Timer

 Buenas!

Y otra entrada más... ¡qué locura!

Bueno, siguiendo con el temario, hoy vamos a ver los puntos 2 y 3 de nuestro índice...

2. Problemas de apilamiento
   2.1  Do(n), pause(n)
   2.2. Do, pause, Do
3. Timer vs. setInterval
   3.1. Callback con parámetros

¿Ready? ¡Vamos allá!




El primer problema (y el que originó todo esto) viene por un problema con el apilamiento de eventos en JavaScript  cuando usamos la función de pausa "setTimeout()".

En un entorno en el que queremos que se repita una acción n veces, esperando t milisegundos cada vez, se define (de manera incorrecta) en JavaScript:

[code lan="javascript"] for(i=0;i<n;i++){ setInterval(t, funcion); } [/code]

Viendo esto, uno puede pensar que puede funcionar, pero... ¡NO! Realmente se están apilando todas las ejecuciones de funcion().

Es decir, se genera un error de concepto.

Lo que pasa realmente es la siguiente secuencia:

 - Aplazamos t milisegundos la ejecución
 - Comprobamos la validación del bucle
 - Aplazamos t milisegundos la ejecución
 ...

Al final, hemos aplazado TODO el bloque de ejecuciones t milisegundos; es decir, como dice el punto 2.1: Do(n), pause(n).

¿Cómo se solucióna?

Hay varias maneras. se puede redirigir el flujo (fue la opción que tomé yo), se puede recalcular el tiempo a cada ejecución... pero al final, necesitamos que nuestro algoritmo haga lo siguiente:

 - Aplazamos t/2 milisegundos la ejecución
 - Esperamos t/2 milisegundos
 - Comprobamos la validación del bucle

Como se puede observar, entre una ejecución y otra esperamos un tiempo, que va haciendo de "separación" entre ejecuciones, obteniendo así un verdadero efecto de temporizador (Do, pause, Do).

Obviamente este método tiene un efecto secundario, y es que no podemos usar tiempos impares (pero, a fin de cuentas ¿A quíen le importa un milisegundo?).

Otra manera (que conlleva otro tipo de limitaciones) es recalcular la espera a cada vuelta, es decir:

 - Aplazamos t * i milisegundos la ejecución
 - Comprobamos la validación del bucle (incrementando i)
 - Aplazamos t * i milisegundos la ejecución
 ...

Pero como comentaba, nosotros usaremos un modelo de flujo partido.

Y con este concepto, vamos a montar un primer esqueleto de nuestro timer:

[code lan="javascript"] sleep = function(){   setTimeout(r_sleep, m); }   r_sleep = function(){   onTick();   setTimeout(sleep, m); } [/code]

donde "ontick()" es nuestra función (que se puede asignar dinámicamente), y "m" son los milisegundos/2 de espera

Con esto finalizamos el punto 2, pero entramos de lleno en el tercero...

¿Qué me dices de la función "setInterval()"?

Sí, existe una función que hace exactamente lo mismo que he escrito arriba, se llama setInterval, y funciona tal que:

setInterval(funcion, tiempo)

Y llama a "funcion" cada "tiempo" milisegundos de manera indefinida.

La principal justificación para usar nuestro método es el valor añadido, cosa que vamos a dedicar un par de entradas.

El primer punto para poder añadir valor a nuestro Timer, es poder pasar parámetros (definidos por el creador de la función de callback) a nuestra función de callback, es decir:


[code lan="javascript"]
sleep = function(){
    setTimeout(r_sleep, m);
}

r_sleep = function(){
    onTick();
    setTimeout(sleep, m);
}

onTick = function(params){
    //aquí podríamos usar "params" como un array que tuviera, por ejemplo,     //el ID de varios elementos a modificar
}
[/code]

Para ello, haremos un simple truco de magia, englobando todo esto en un objeto (Timer), de manera que tengamos un contexto propio:

[code lan="javascript"] function Timer(milis, params, onTick){     this.m = milis;     this.p = params;     this.onTick = onTick; }   Timer.prototype.sleep = function(){     setTimeout(this.r_sleep, this.m); }   Timer.prototype.r_sleep = function(){     this.onTick(this.p);     setTimeout(this.sleep, this.m); } [/code]

De manera que podamos hacer un objeto al que le pasemos un array p de parámetros que llegará a onTick y podremos usar a nuestro antojo. Todo correcto, ¿no?

¡PUES NO!

Si hacéis esto veréis que no funciona... para solucionarlo tendréis que esperar a la próxima entrada ;-)

¡Nos vemos!