Web Workers en JavaScript: ejemplos y explicación

Introducción

Recuerdo que utilicé los Web Workers en JavaScript una vez que tenía que generar un PDF muy grande. Así, aunque fuera un proceso tardado, la interfaz de usuario no se quedaba congelada.

Veamos entonces un demo o ejemplo y la explicación de los web workers o webworkers en el lenguaje de programación JavaScript

Probar proyecto terminado

Si quieres ver la diferencia entre usar o no usar los Web Workers aquí dejo estos enlaces para calcular números primos, cosa que es muy tardada.

  1. Con Web worker
  2. Sin Web Worker

Sigue leyendo si quieres saber más.

¿Qué es un Web Worker en JavaScript?

Es una nueva (no tan nueva pero su uso no está tan extendido) API que nos permite ejecutar procesos en segundo plano. Recordemos por ejemplo las llamadas asíncronas, se hacen para no congelar la interfaz del usuario.

Por ejemplo, si no existiera el código asíncrono e hiciéramos una petición GET cuya duración fuera de 10 segundos, el usuario no podría hacer nada (no hacer click en botones, no ver animaciones) por 10 segundos.

Entonces cuando hacemos un proceso pesado o largo que no puede ser asíncrono, usamos los Web Workers en Javascript.

¿Cómo funcionan los web workers en JavaScript?

Se comunican a través de mensajes, así como cuando llamamos a funciones. Para pasarles datos llamamos a la función postMessage.

Y cuando el worker quiere hablarnos, dispara el evento onmessage.

Ah, antes de que lo olvide. Los Workers no pueden manipular directamente al DOM, sólo pasan o reciben mensajes y procesan datos.

Ejemplo de uso de Web WorkerS en JavaScript

Vamos a calcular números primos. Mejor dicho, buscaremos cuál es el número primo más grande menor al número 150, 000.

Números primos

Los números primos son aquellos que sólo son divisibles entre 1 y entre ellos mismos.

Por ejemplo, el 11 es primo porque no puede ser dividido entre 2, ni entre 3, 4, 5, 6, 7 8, 9 o 10. Se dice que “puede ser dividido” cuando su residuo es cero.

Pero no vamos a ver el algoritmo, vamos a calcular números primos “a lo tonto” para que sea un proceso tardado. Me he robado de por ahí un algoritmo para determinar si un número es primo:

function primo(numero) {

  for (var i = 2; i < numero; i++) {

    if (numero % i === 0) {
      return false;
    }

  }

  return numero !== 1;
}

Ahora en el Web Worker vamos a comprobar cuál es el mayor número primo menor a 150,000

Programación del worker para el cálculo

El código completo del worker queda así:

/**
 * Ejemplo de Web Worker calculando primos
 * @author parzibyte.me/blog
*/

/*
Esto será ejecutado únicamente cuando le pasamos datos al worker

Si no queremos llamarlo y queremos que se ejecute tan pronto lo instanciamos, 
simplemente quitamos la función y dejamos todo el código afuera. 

La ventaja de esperar a onmessage es que podemos recibir parámetros
*/

onmessage = function(evento){
	var limite = evento.data; // Lo que recibimos de la llamada
	var mayorEncontradoHastaElMomento = 0; // Aquí guardaremos el mayor

	function esPrimo(numero) {

	  for (var i = 2; i < numero; i++) {

	    if (numero % i === 0) {
	      return false;
	    }

	  }

	  return numero !== 1;
	}

	for(var x = 0; x < limite; x++){
		if(esPrimo(x)){
			mayorEncontradoHastaElMomento = x;
		}
	}

	//Cuando termine el for, llamamos al método
	// postMessage para informar a nuestro invocador
	// que hemos terminado nuestra larga e importante tarea
	postMessage("Hola. El número primo más grande menor que " + limite.toString() + " es " + mayorEncontradoHastaElMomento.toString());

}

Como vemos es un ciclo for, vamos comprobando por cada número si éste es primo y en caso de que sí, lo tomamos como el mayor encontrado.

Cuando salimos del ciclo for llamamos a la función postMessage. Esta función es la más importante, pues sirve para enviar mensajes ya sea del hilo principal al worker, o del worker al hilo principal.

También es importante la función onmessage, pues es llamada cuando recibimos un mensaje.

Llamada e inclusión del web worker en una página web

Ahora en nuestro hilo principal (esto es en nuestra página HTML) tenemos este código:

<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="UTF-8">
	<title>Calcular número primo con Web Workers</title>
</head>
<body>
	<h1>Vamos a esperar la respuesta del Web Worker</h1>
	<p>El botón de abajo es para demostrar que podemos hacer click porque no es código bloqueante</p>
	<button>Soy un botón y puedes hacerme click cuando quieras</button>
	<p id="resultados">Aquí aparecerá el mensaje</p>
</body>

<script>
	//Primero vemos si el navegador lo soporta
	if("undefined" !== typeof Worker){
		var miWorker = new Worker("worker.js"); // Como argumento le pasamos la ruta del script

		miWorker.postMessage(150000); // Le decimos que calcule el mayor primo menor que 150000
		miWorker.onmessage = function(evento){
			var elemento = document.querySelector("#resultados"); // Obtener el párrafo

			//Por cierto, el mensaje que pasamos está en data
			elemento.innerHTML = evento.data;
		}
	}
</script>
</html>

Por favor lee el código, tiene comentarios que lo explican.

Es importante mencionar que debemos servir la página en HTML en un servidor, puede ser Apache sobre Windows o cualquiera que prefieras.

El punto es que no debemos abrirlo con file:///, sino con un servidor HTTP.

Probando web worker

Ahora cuando navegamos a la página en un inicio se ve esto (al menos en mi lenta computadora):

Web Worker trabajando en segundo plano
Web Worker trabajando en segundo plano

Pero después de unos segundos (si tu computadora es muy rápida puedes hacer más grande el número, y si es más lenta, lo contrario) aparece el mensaje:

Mensaje del web worker
Mensaje del web worker

No sé si es cierto que el número es 149, 993. Lo que sí sé es que el worker se ejecutó con éxito en segundo plano sin bloquear mi interfaz.

Ahora se me ocurre algo muy interesante, ¿qué pasaría si no usamos Web Workers en JavaScript? veamos cómo no podremos hacer click en el botón.

Si los workers no existieran…

Veamos entonces cómo hacer lo mismo pero sin web worker. Todo quedaría en el mismo archivo:

<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="UTF-8">
	<title>Calcular número primo sin Web Workers</title>
</head>
<body>
	<h1>Vamos a esperar a que el cálculo termine</h1>
	<p>El botón de abajo es para demostrar que NO podemos hacer click porque SÍ es código bloqueante</p>
	<button>Soy un botón y NO puedes hacerme click mientras se trabaja</button>
	<p id="resultados">Aquí aparecerá el mensaje</p>
</body>

<script>
	//Este setTimeout es para esperar 2 segundos antes de ejecutar todo el código de abajo
	// De esta forma demostramos que primero todo va bien pero luego la interfaz "se congela"
	window.setTimeout(function(){
		var limite = 150000; // Lo definimos desde aquí
		var mayorEncontradoHastaElMomento = 0; // Aquí guardaremos el mayor

		function esPrimo(numero) {

		  for (var i = 2; i < numero; i++) {

		    if (numero % i === 0) {
		      return false;
		    }

		  }

		  return numero !== 1;
		}

		for(var x = 0; x < limite; x++){
			if(esPrimo(x)){
				mayorEncontradoHastaElMomento = x;
			}
		}

		var elemento = document.querySelector("#resultados"); // Obtener el párrafo

		//Escribir el resultado
		elemento.innerHTML = "Hola. El número primo más grande menor que " + limite.toString() + " es " + mayorEncontradoHastaElMomento.toString();
	}, 2000);
</script>
</html>

Usamos un setTimeout para esperar antes de ejecutar el proceso pesado. Así, al inicio sí podemos clickear al botón pero luego de 2 segundos no podemos hacer nada hasta que el proceso termina.

Esta es la gran ventaja de los web workers en JavaScript: que no son bloqueantes y procesan tareas difíciles en un hilo diferente al principal.

Repaso y conclusión

  • Sirven para ejecutar tareas costosas en segundo plano
  • Reciben mensajes a través del evento onmessage. Y envían mensajes con postMessage. Igualmente podemos llamar al método postMessage para llamar al worker, y escuchar su evento onmessage para cuando él nos hable
  • No están disponibles en todos los navegadores aunque sí en la gran mayoría

Recuerda que puedes probar los ejemplos directamente en tu navegador

  1. Con Web worker
  2. Sin Web Worker

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

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