Introducción a WebWorkers

Introducción a WebSocket

 

Los WebWorkers son una de las nuevas características de JavaScript que permite crear aplicaciones multiprocesos. Desde su inicio JavaScript nació con el objetivo de proporcionar una ayuda en el desarrollo de aplicaciones web, sin embargo, desde su origen nunca se pensó que JavaScript llegara hacer lo es hoy, un lenguaje de propósito general que van desde la vista, hasta servir como un lenguaje de programación completo en el BackEnd; NodeJS es un claro ejemplo de esto.

Una de las grandes limitantes de JavaScript era su incapacidad de trabajar con multitareas, lo que obligaba a que todas las funciones que ejecutábamos en JavaScript tenían que concluir antes de que otra pudiera iniciar. Es por este motivo que se decide crear los WebWorkers.

 

WebWorkers

 

Mediante los WebWorkers se supera la incapacidad de JavaScript de trabajar con multiprocesos, llevado a la WEB a un nuevo nivel, incluso pudiendo competir con las aplicaciones de escritorio ya tanto conocemos.

 

Implementando los WebWorkers

 

Como podemos ver en la imagen anterior, para crear un WebWorker es necesario de al menos dos archivos de JavaScript, ya que todo el código que va a ser ejecutado como un hilo deberá esta de forma independiente en un Script, y será necesario un segundo archivo JavaScript el cual mande llamar al primero, pero este no es solo para eso, si no que mediante este segundo archivo podremos procesar los avances que nos valla reportando el Worker, ya que el Worker no tiene visibilidad de nada de lo que este por fuera del Worker, por lo que el acceso al DOM y sus elementos, estará limitado, es por ello que este segundo Script será necesario.

 

WebWorkers

 

Así pues, al script que contiene la lógica del worker (Verde) lo llamaremos simplemente Worker, por otro lado, al script que ejecuta al worker lo llamaremos WorkerManager(Amarillo). Cabe mencionar que yo lo estoy nombrando de esta manera para identificarlos, por lo que no es como tal una nomenclatura.

 

Como vemos en la imagen, es el WorkerManager quien ejecuta un nuevo hilo de procesamiento a través del Worker, y este último inicia con la tarea que le es encomendada, después, el Worker puede enviar una serie de actualizaciones al WorkerManager indicándole el avance del procesamiento o la finalización del mismo. El WorkerManager es el encargado de actualizar la UI para mostrar los avances del Worker.

 

Creando nuestro primer WebWorker

 

Desarrollaremos un pequeño ejemplo que nos ayude entender mejor como es que los WebWorkers trabajan, para ello crearemos una pequeña aplicación, la cual cree serie de barras de progreso, las cuales por medio de JavaScript las iremos llenando hasta alcanzar el 100%, simulando que estamos realizando alguna tarea pesada.

 

WebWorkers

 

Este proyecto consta de tres archivos que corresponde con la página HTML, el WorkerManager y el Worker. En la, por otra parte. Todo el proyecto lo puedes descargar desde el GitHub: https://github.com/oscarjb1/WebWorker.git

 

Archivo Index.html:

página HTML tenemos la estructura base del proyecto y algunos estilos CSS para darle una mejor presentación.

<!DOCTYPE html>
<html>
 <head>
 <title>WebWorker con HTML5</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <script type="text/javascript" src="WorkerMananger.js"></script>
 <style>
 body{
 background-color: #fafafa;
 }
 #workerDesk{
 position: relative;
 width: 90%;
 margin :auto;
 }
 input{
 display: block;
 }
 .worker{
 position: relative;
 display: inline-block;
 margin: 10px 10px;
 padding: 10px 15px;
 border: 2px solid #cacaca;
 width: 30%;
 box-sizing: border-box;
 border-radius: 5px;
 box-shadow: 0 0 5px rgba(0,0,0,0.3);
 }
 .worker > p{
 margin: 0px;
 }
 </style>
 </head>
 <body>
 <div id="workerDesk">
 <h1>WebWorker test</h1>
 <input id="btnSubmit" type="button" value="Add Worker" onclick="startWorker();"/>
 </div>
 </body>
</html>

 

Como podemos apreciar, la página HTML solamente tiene los contenemos donde aparecerán los Workers y un título, adicional a eso, podemos ver en el Header que importa al archivo de JavaScript WorkerMananger.js.

 

Worker

Archivo donde simularemos una tarea y enviaremos actualizaciones para actualizar la UI

addEventListener("message", start, false);

function start(e) {
 var workerName = e.data;
 for (var c = 1; c <= 100; c++) {
 postMessage("{\"workerName\" : \""+workerName+"\", \"progress\" : "+c+"}");
 sleep(100);
 }
}


function sleep(milliseconds) {
 var start = new Date().getTime();
 for (var i = 0; i < 1e7; i++) {
 if ((new Date().getTime() - start) > milliseconds) {
 break;
 }
 }
}

Este archivo contiene en la primera línea el registro al evento “message”, en el cual le indicamos que cuando un nuevo mensaje le sea enviado por el WorkerManager, este ejecutara la función start.

La función start realiza un ciclo del 1 al 100 simulando el avance de una tarea, en cada iteración el Worker envía un nuevo mensaje a WorkManager con el id del Worker y el porcentaje avanzado. Seguido ejecuta al método sleep que se utiliza para dormir el hilo cada 100 milisegundos entre iteración.

 

WorkManager

Archivo desde donde iniciaremos los Workers y procesaremos los avances que publiquen los Workers a medida que avanzan.

window.addEventListener("load",init,false);

function init(){
 counter = 1;
}

function startWorker(){
 var workerDesk = document.getElementById("workerDesk");
 var workerName = "progress-"+counter;
 var titleName = "title-" +counter;
 
 var worker = document.createElement("div");
 worker.setAttribute("class", "worker");
 
 var title = document.createElement("p");
 title.setAttribute("id",titleName);
 title.innerHTML = workerName + " (0%)";
 worker.appendChild(title);
 
 
 var progress = document.createElement("progress");
 progress.setAttribute("id",workerName);
 progress.setAttribute("value","0");
 progress.setAttribute("max","100");
 worker.appendChild(progress);
 
 
 workerDesk.appendChild(worker);
 
 var worker = new Worker("Worker.js");
 worker.addEventListener("message", updates, false);
 worker.postMessage(counter);
 counter++;
 
}

function updates(e){
 var response = JSON.parse(e.data); 
 var progress = document.getElementById("progress-" + response.workerName);
 progress.value = response.progress ;
 
 var title = document.getElementById("title-" + response.workerName);
 title.innerHTML = "Worker-" + response.workerName + " ("+response.progress+"%)";
 
}

 

Primero que nada, tenemos el método startWorket que es ejecutado tras presionar el botón del formulario. No quisiera entrar en muchos detalles, pues aquí lo que realizamos es armar la UI y agregar un nuevo progress bar por cada Worker. En lo que si me quisera detener es al final del método, en donde creamos el Worker mediante new Worker(“Worker.js”), es aquí donde le decimos que el Worker deberá tomar el archivo Worker.js y lo ejecute, seguido registramos un evento para recibir los avances mediante el método updates. Y finalmente, mediante la función postMessage, le enviamos un mensaje al Worker para que inicie con el procesamiento.

Por último, tenemos el método updates, el cual recibirá un evento cada vez que el Worker publique un avance, en este método solo actualizamos la GUI con el progreso recibido.

Conclusiones:

Como pudimos comprobar, los WebWorker nos permite crear hilos independientes que pueden realizar tareas de forma independiente. Una de las cosas que tenemos que tener en cuenta es que, los hilos que se crean, son hilos reales del sistema operativo, por lo que no utilizarlos correctamente pueden dañar el performance del sistema operativo del cliente.

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *