Alters

Timer.js: Arreglando y mejorando

¿Y esto qué es?

Sí! Vamos retomando el ritmo en el blog... y es que resulta que Meryhell estudia por las tardes y en el rato que hay entre que llego de trabajar y llega ella lo tengo como "rato muerto", y, oye... hay que aprovecharlo!

Así que aquí me tenéis, retomando el blog... que ya era hora :D

Y bueno, después del típico rodeo... a lo que vamos, ¿no? Hoy os traigo la tercera parte del proyecto del timer.

En esta entrada vamos a arreglar y mejorar (como dice el título) nuestro timer, ya que en la anterior entrada lo dejamos casi funcional.

¡Vamos allá!




Tenemos aquí nuestro índice:


  1. Concepto de Timer
  2. Problemas de apilamiento
    1. Do(n), pause(n)
    2. Do, pause, Do
  3. Timer vs. setInterval
    1. Callback con parámetros
    2. ¿Problemas con la referencia?
      1. Objeto global
    3. setTimeout con parámetros
      1. Multiinstancia
En esta entrada veremos desde el punto 3.2 (¿Problemas con la refencia?).

Pues bien, si recordáis, dije que la clase que creamos no sería funcional, y resulta que, evidentemente, el problema viene con la referencia.

Cada "setTiemout" ejecuta una función anónima, es decir, no tiene en cuenta partes del contexto. Por eso, cuando se hace una vuelta completa (sleep, r_sleep), al volver a "sleep", no va a encontrar la refencia a "this".

¿Y cómo lo arreglamos? Pues como sugiere el punto 3.2.1, usando un objeto global. Es decir, algo como:

[code lan=gral] var t1 = new Timer(100, null, onTick); onTick = function(){     //aquí hacemos cosas }   function Timer(milis, params, onTick){     this.m = milis;     this.p = params;     this.onTick = onTick; }   Timer.prototype.sleep = function(){ //  setTimeout(this.r_sleep, this.m);     setTimeout(t1.r_sleep, t1.m); }   Timer.prototype.r_sleep = function(){ //  this.onTick(this.p); //  setTimeout(this.sleep, this.m);     t1.onTick(t1.p);     setTimeout(t1.sleep, t1.m); } [/code]
Ahora sí, cambiando la referencia a una instancia global del objeto, siempre tendremos el contexto, por lo que no tendremos problemas a la hora de acceder a los atributos del objeto.

Pero este arreglo, a parte de feo es poco útil, ya que si definimos dos instancias de Timer, siempre va a usar los atributos de la instancia que se llame "timer1" (y puede que ni exista), así que... ¿qué hacemos?

Pues hay una solución. Resulta que la función "setTimeout" acepta n parámetros de manera que su estructura es:

setTimeout(funcion, tiempo, p1, p2, p3, ..., pn). Cada Pn corresponde a un parámetro de entrada para "funcion", por lo que podemos acabar de arreglarlo todo de la siguiente manera:


[code lan=gral] var t1 = new Timer(100, null, onTick); onTick = function(){     //aquí hacemos cosas }   function Timer(milis, params, onTick){ //  me había olvidado, tenemos que partir los milisegundos entre 2     this.m = milis / 2;     this.p = params;     this.onTick = onTick; }   Timer.prototype.sleep = function(t){ //  setTimeout(this.r_sleep, this.m); //  setTimeout(t1.r_sleep, t1.m);     setTimeout(t.r_sleep, t.m); }   Timer.prototype.r_sleep = function(t){ //  this.onTick(this.p); //  setTimeout(this.sleep, this.m); //  t1.onTick(t1.p); //  setTimeout(t1.sleep, t1.m);     t.onTick(t.p);     setTimeout(t.sleep, t.m); } [/code]

De esta manera, vamos pasando la referencia a la instancia en cada llamada, lo que nos permite aislar el código y a la vez nos da paso a poder hacer varias instancias del mismo objeto en el mismo script.

Como final, voy a comentar algunas mejoras que he añadido a este timer, y que por motivos de espacio voy a explicar pero no a exponer (aunque sí voy a dejar un enlace al fuente).

 - Tiempo máximo de ejecución: Se puede ajustar un tiempo máximo de ejecución (recomendable) para evitar bucles infinitos

 - Ticks mínimos: Se puede ajustar un número mínimo de "ticks", para garantizar el funcionamiento.

 - Evento previo a iniciar: Se crea un método que se ejecuta antes del primer "sleep".

 - Evento previo a r_sleep: se crea un método que se dispara antes de r_sleep.

 - Evento posterior a r_sleep: Se crea un método que se dispara después de r_sleep.

 - Evento previo a finalizar: Se crea un método que se lanza antes de "apagar" el timer.


Todos estos eventos son opcionales, y se pueden ajustar a cualquier función con parámetros, de manera que es el programador el que determina qué se hará en cada etapa del timer.

Os dejo aquí el enlace al fuente completo, y con esto dejamos todo preparado para la próxima entrada.


¡Hasta la próxima