Pre-Order Kelboy 2.1 Kit

Tienes 3 kits a elegir, aprovecha antes de agotar stock, las primeras unidades tienen envío rápido.

Desarrolla en javascript con la función sleep

Debido a que javascript no tiene esta función, este tutorial trata de explicar y cómo implementar la tradicional función "sleep" en javascript para poder usarla en los desarrollos web que lo requieran.

Advance
8


¿Cuál es el problema?

JavaScript no tiene una función sleep, la cual se encarga de que el código espere un período de tiempo específico antes de reanudar la ejecución.

Entonces, ¿qué debe hacer si necesita JavaScript para esperar? La respuesta es que javascript tiene una función parecida, llamada setTimeout().

El método setTimeout() del mixin WindowOrWorkerGlobalScope establece un temporizador que ejecuta una función o una porción de código después de que transcurre un tiempo establecido. Ésta utiliza dos parámetros, la función o sentencia a ejecutar, y el tiempo que ha de esperar para ser ejecutado, además de los parámetros si los hay.

En resumen, esta función no interrumpe el programa, sino que lanza un trigger de un código cuando se le indique.

Entonces, ¿cuál es el problema? El problema viene a que no interrumpe, ni espera una señal, ni hace "dormir" al hilo desde el que se invoca, el que está ejecutándose. Esto puede llegar a ser un problema si estás utilizando una serie de librerías que utilizan frameworks como JQuery internamente y no te permiten refrescar ciertos hilos de ejecución aunque tú estés forzando una actualización en el propio javascript.

Toda esta información la podéis encontrar en la parte de desarrolladores de Mozilla, una de las mejores fuentes oficiales de documentación sobre el motor javascript que podéis encontrar en la web en el siguiente enlace.


Javascript es un lenguaje basado en prototipos

Javascript es un lenguaje de alto nivel, que no sólo se utiliza en navegadores web modernos, si no que se encuentra en multitud de plataformas y entornos modernos. ECMAScript es el estándar registrado y monitorizado a través de la organización internacional ECMA.

También podréis ver una amplia documentación multiidioma en la página de desarrolladores de Mozilla , tiene un buscador muy sencillo de utilizar en el que encontraréis multitud de artículos profesionales, pero si lo usáis como hemos referenciado antes, no encontraréis la función sleep ni nada relacionado con ella

Pero podréis documentaros sobre el lenguaje JavaScript y cómo funciona, uno de esos artículos interesantes para cualquier programador de este lenguaje habla sobre que JavaScript es un lenguaje confuso para programadores de lenguajes clásicos (como C++ o Java) ya que se basa en el prototipado.

Una característica interesante de ello es que todos los objetos en JavaScript son instancias de Object, el cual se encuentra en la escala primera más alta de la cadena de prototipos, y a diferencia de los lenguajes de programación orientada a objetos, las clases no existen (hasta 2015 que se agregan como plantillas para crear objetos), son también construidas sobre prototipos (aunque su sintaxis haga deducir que tienen la misma semántica que el concepto clásico de clases). Por ello se deduce que cualquier atributo o función es un prototipo, es decir, cualquier función puede ser añadida como un objeto en forma de propiedad. Sólo existe un elemento que no es un prototipo, null, el cual es considerado como el elemento final de la cadena de prototipado.

Sabiendo esto, y nuestras dotes de programación, si todas las funciones, métodos y objetos de este lenguaje son prototipos, y nosotros como desarrolladores podemos declarar y construir los prototipos que querramos, es lógico que podamos implementar el método sleep que el lenguaje no incluye, o por lo menos simularlo.


¿Cómo implementar sleep para mi día a día?

Lo primero que debemos saber para usar nuestra deseada función es cómo funciona, y encontrar una manera de implementarla.

Una manera de alcanzar esta meta es entender cómo funciona una implementación en varios hilos y cómo hacer que determinadas funciones corran en background.

JavaScript incluye las definiciones de await, async, pero para entenderlas debes de comprender cómo funciona un Worker. Una vez entendidos los conceptos básicos podemos deducir que un Web Worker es un script que puede correr en un hilo en background. Una implementación que usamos los desarrolladores web cada día son los eventos, los cuales de manera asíncrona, hacen ejecutar una función que hemos programado en un determinado momento.

Un sencillo ejemplo puede ser el siguiente:

onmessage = function(e) {
  console.log('Worker: Message received from main script');
  const result = e.data[0] * e.data[1];
  if (isNaN(result)) {
    postMessage('Please write two numbers');
  } else {
    const workerResult = 'Result: ' + result;
    console.log('Worker: Posting message back to main script');
    postMessage(workerResult);
  }
}

Bien, comprendida esta parte, hemos sido capaces de asimilar que podemos crear tareas que corran el hilos diferentes, lo cual nos permitiría separar en diferentes hilos nuestro código a ejecutar, pudiendo enviar mensajes para que pare y reanude, y por lo tanto podemos realizar teóricamente una implementación del método sleep usando la ya existente función setTimeout dentro de un Worker que se ejecute en background.

Sólo nos queda solucionar una pequeña parte del puzzle, hacer que mi implementación de "sleep" devuelva el control al hilo principal cuando termine de ejecutarse.

Una manera que se nos ocurre de implementar esta solución es usar Promise. Éste gran desconocido es el que nos proporciona la capacidad de devolver un valor en una ejecución async entre un hilos. Básicamente nos permite compartir los handlers en una implementación de Proxy. Un Proxy es un objeto que envuelve cualquier otro objeto para ser usado con un manejador, y un manejador o handler es la función que define cómo ha de comportarse en determinados momentos.

El siguiente ejemplo arroja un poco de luz de cómo funciona un Proxy y su handler, y que el handler define cómo ha de comportarse cuando se lee, a pesar de su definición interna, es el siguiente:

const target = {
  notProxied: "original value",
  proxied: "original value"
};

const handler = {
  get: function(target, prop, receiver) {
    if (prop === "proxied") {
      return "replaced value";
    }
    return Reflect.get(...arguments);
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.notProxied); // prints "original value"
console.log(proxy.proxied);    // prints "replaced value"

No os asusteis, ésta no es más que la explicación de cómo funciona por dentro un Proxy, ya que nosotros no queremos más que usar Promise, y él es un Proxy, sólo que tiene una peculiaridad de unir estos manejadores entre hilos, lo que nos permite alterar el comportamiento de otro hilo desde una función que declaremos.

Esto es lo que deseamos hacer:

const sleepNow = (delay) => new Promise((resolve) => setTimeout(resolve, delay))

Esta simple línea, añadida en alguna de nuestras librerías, hace que la magia funcione sobre un prototipo async llamado desde una función con await.

Un extracto, de un ejemplo real en una librería javascript propia en el que se dibujan gráficos, es el siguiente:

...
a.prototype.launch = async() => {
  
  var dataStore = getDataStore();
  document.getElementById('my-label').innerHTML = "Datos obtenidos...";
  for (const [key, value] of Object.entries(dataStore)) {

    for(var i=0;i<value.length;i++){
      $("#div-"+key).append("<div class='row'><div class='col-12'><div id='linechart-line-div-"+i+"-"+key+"' /></div></div>");
      createLineChart("linechart-line-div-"+i+"-"+key, value[i], "y", ["Date"], ["Temperature"], ["0.9"], ["#ffffff"], ["#999999"], ["#188ae2"]);
      percentage += (100/value.length);
      $("#progress-bar-div")[0].style.width = percentage+"%";
      document.getElementById('my-label').innerHTML = "Procesando datos de '"+key+"' ...";
      await sleep(10);
    }
  }

  $("#progress-bar-div").css("width","100%");
  document.getElementById('h4-label').innerHTML = "Todos los datos han sido procesados";
}

La función createLineChart hace una gran carga sobre el DOM, y no permite refrescar correctamente al navegador el estado hasta que se produce una pequeña interrupción, con nuestra implementación de sleep casera conseguimos esa ventana de ejecución para que pueda refrescarse, actualizar la barra de porcentaje y mostrar el estado al usuario.