Articles

JavaScript Web Workers: Un principiante' s Guide

En 2019, el ecosistema web ha evolucionado hasta el punto en que el navegador es un entorno de ejecución para aplicaciones basadas en JavaScript. Esto se refleja en la velocidad con la que la industria crea nuevos marcos, paradigmas, cargadores de módulos y paquetes, administradores de dependencias, herramientas de compilación y administradores de paquetes año tras año.

Cuando se concibió JavaScript en los primeros días de Internet, la dirección del desarrollo de Internet no estaba clara. Debido al cambio constante y rápido en la industria y el ecosistema, la necesidad de compatibilidad con versiones anteriores de los navegadores y los estándares web, la evolución de JavaScript se convirtió en un flujo constante de parches, hacks y reflexiones de última hora.

Los trabajadores web de JavaScript cargan

Los dispositivos móviles de hoy en día normalmente vienen con más de 8 núcleos de CPU o más de 12 núcleos de GPU. Las CPU de escritorio y servidor tienen hasta 16 núcleos, 32 subprocesos o más.

En este entorno, tener un entorno de programación o scripting dominante que sea de un solo subproceso es un cuello de botella.

JavaScript Es un subproceso único

Esto significa que, por diseño, los motores JavaScript, originalmente navegadores, tienen un subproceso principal de ejecución, y, en pocas palabras, el proceso o la función B no se pueden ejecutar hasta que el proceso o la función A hayan finalizado. La interfaz de usuario de una página web no responde a ningún otro procesamiento JavaScript mientras está ocupada ejecutando algo, lo que se conoce como bloqueo DOM.

Esto es terriblemente ineficiente, especialmente en comparación con otros idiomas.

Si vamos a JS Bin y ejecutamos este código en la consola JavaScript del navegador:

//noprotecti = 0;while (i < 60000) { console.log("The number is " + i); i++;}

… todo jsbin.com sitio web no responderá hasta que el navegador de cuenta y registros — a 60.000.

No podremos interactuar con nada de la página, porque el navegador está ocupado.

Ahora, este es un proceso informático relativamente poco exigente, y las aplicaciones web de hoy en día a menudo implican tareas mucho más exigentes.

Necesitamos ser capaces de calcular cosas en segundo plano mientras el usuario interactúa sin problemas con la página.

Web Workers

El W3C publicó un primer borrador del estándar web workers en 2009. La especificación completa se puede encontrar en el sitio web del Grupo de Trabajo de Tecnología de Aplicaciones de Hipertexto Web, o WHATWG, un organismo de estándares web alternativo al W3C.

Web workers es un sistema o protocolo asincrónico para que las páginas web ejecuten tareas en segundo plano, independientemente del hilo principal y la interfaz de usuario del sitio web. Es un entorno aislado que está aislado del objeto window, el objeto document, acceso directo a Internet y es el más adecuado para tareas computacionales exigentes o de larga duración.

Además de los trabajadores web, un sistema dedicado a la multiproceso, hay otras formas de lograr un procesamiento asincrónico en JavaScript, como las llamadas Ajax asincrónicas y el bucle de eventos.

Para demostrar esto, volveremos a JS Bin y probaremos este fragmento de código:

console.log("A");setTimeout(function(){console.log("B");},2000);console.log("C");setTimeout(function(){console.log("D");},0);console.log("E");setTimeout(function(){console.log("F");},1000);

Cuando ejecutemos esto, nuestra secuencia de registro es A, C, E, D, F, B. El navegador primero programa las operaciones sin el tiempo de espera, a medida que vienen, y luego ejecuta las funciones setTimeout() en el orden de sus respectivos retrasos especificados. Sin embargo, esta asincronicidad no debe combinarse automáticamente con el multihilo. Dependiendo de la máquina host, esto a menudo puede ser solo una pila de un solo hilo de las llamadas en el orden que explicamos.

Web Workers & Multiproceso

Como explica el sitio web de referencia de JavaScript de Mozilla, los web workers son un » medio para que el contenido web ejecute scripts en subprocesos en segundo plano.»

Los usamos de la siguiente manera: comprobamos la disponibilidad del constructor Worker() en el navegador, y si está disponible, creamos una instancia de un objeto worker, con la URL del script como argumento. Este script se ejecutará en un subproceso separado.

El script debe servirse desde el mismo host o dominio por razones de seguridad, y esa es también la razón por la que web workers no funcionará si abrimos el archivo localmente con un esquema file://.

if (typeof(Worker) !== "undefined") { worker = new Worker("worker.js");} 

Ahora vamos a definir este código en el worker.js archivo:

i = 0;while (i < 200000) { postMessage("Web Worker Counter: " + i); i++;}

Si desea escribir archivos de trabajo web de JavaScript de alta calidad, consulte nuestro libro, JavaScript: Best Practice.

La separación de hilos

Una cosa importante a tener en cuenta aquí es la separación del window y document ámbito de ejecución en el hilo de la ventana principal del navegador, y el ámbito worker.

Para hacer uso del hilo worker, estos dos ámbitos deben poder comunicarse. Para lograr esto, utilizamos la etiqueta postMessage() función dentro de la etiqueta worker.js archivo — enviar mensajes al navegador principal hilo — y el worker.onmessage escucha en el hilo principal para escuchar worker mensajes.

También podemos enviar mensajes desde el hilo principal del navegador al hilo o función worker. La única diferencia es que invertimos las cosas y llamamos a worker.postMessage() en el hilo principal, y onmessage en el hilo de trabajo. Para citar la referencia para desarrolladores de Mozilla:

Observe queonmessage ypostMessage() deben colgarse del objetoWorker cuando se usan en el hilo de script principal, pero no cuando se usan en el worker. Esto se debe a que, dentro del trabajador, el trabajador es efectivamente el alcance global.

Podemos usar el método terminate() de la misma manera, para finalizar la ejecución de nuestro trabajador.

Con todo esto en mente, hemos venido a este ejemplo:

índice.html

<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Web Workers Example</title> <style type="text/css"> body {padding-top:28px;} .output-cont {margin-left:12%; margin-top:28px;} .output-cont h3 {width:200px; height:100%;} .output-cont button {padding:4px 8px; font-size:1.1rem; font-family:sans-serif; } </style></head><body><div class="output-cont"><button onclick="testWorker()">start worker</button><h3></h3><button onclick="terminateWorker()">terminate worker</button></div><br/><div class="output-cont"><button onclick="testMainThread()">start blocking thread</button><h3></h3></div><br/><div class="output-cont"><button onclick="alert('browser responsive!')">test browser responsiveness</button></div> <script> var worker; function testWorker() { if (typeof(Worker) !== "undefined") { if (typeof(worker) == "undefined") { worker = new Worker("worker.js"); } worker.onmessage = function(event) { document.getElementById("workerOutput").innerHTML = event.data; }; } else { document.getElementById("workerOutput").innerHTML = "Web Workers are not supported in your browser"; } } function terminateWorker() { worker.terminate(); worker = undefined; } function testMainThread() { for (var i = 0; i < 200000; i++) { document.getElementById("mainThreadOutput").innerHTML = "Main Thread Counter: " + i; } } </script></body></html>

y worker.js:

i = 0;while (i < 200000) { postMessage("Web Worker Counter: " + i); i++;}

Esto nos da la oportunidad de probar los efectos de la ejecución del hilo principal en el comportamiento y el rendimiento de la página frente a los efectos del trabajador web.

En este tutorial, usamos http-server para servir los archivos localmente.

JavaScript web workers testing

Ahora podemos ver que el hilo de trabajo no bloquea la interactividad del proceso del navegador principal, y el bucle a través de 200.000 números no afecta al hilo principal. Los números del elemento #workerOutput se actualizan en cada iteración.

El hilo de bloqueo, o hilo principal, cuando está conectado a un bucle, bloquea toda la interactividad (hemos establecido el número de iteraciones en 200.000 aquí, pero será aún más obvio si lo aumentamos a 2.000.000).

Una cosa más que nos apunta a un hilo principal bloqueado es que el proceso de trabajo actualiza la página en cada iteración, y el bucle en el hilo principal (el definido en index.html) solo actualiza el elemento #mainThreadOutput en la última iteración.

Esto se debe a que el navegador está demasiado consumido por el conteo (for bucle) para poder volver a dibujar el DOM, por lo que lo hace solo una vez que su negocio con el bucle for está completamente hecho (al final del bucle).

Conclusión

En este artículo, presentamos web workers, una tecnología que ayuda a la industria web a mantenerse al día con las aplicaciones web cada vez más exigentes. Esto se hace proporcionando una forma para que las aplicaciones web aprovechen los dispositivos multiprocesador y multiprocesado otorgando algunos superpoderes multiprocesados a JavaScript.

Los trabajadores web convierten los entornos de navegadores móviles y de escritorio en plataformas de aplicaciones, proporcionándoles un entorno de ejecución estricto. Este rigor puede obligarnos a prever la copia de objetos entre múltiples hilos y a planificar nuestras aplicaciones teniendo en cuenta estas restricciones.

Deja una respuesta

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