Capítulo 14 API Web Workers
JavaScript se ha convertido en la principal herramienta para la construcción de aplicaciones exitosas en Internet. Como explicamos en el Capítulo 4, ya no es solo una alternativa para la creación de llamativos (a veces irritantes) trucos para la web. El lenguaje se ha vuelto una parte esencial de la web y una tecnología que cada desarrollador necesita entender e implementar.
Javascript ya ha alcanzado el estado de lenguaje de propósito general, una condición en la cual es forzado a proveer características elementales que por naturaleza no posee. Este lenguaje fue concebido como un lenguaje interpretado, creado con la intención de ser procesado un código a la vez. La ausencia de multiprocesamiento en Javascript (procesamiento de múltiples códigos al mismo tiempo) reduce su eficiencia, limita su alcance y vuelve a algunas aplicaciones de escritorio imposibles de emular en la web.
Web Workers es una API diseñada con el propósito específico de convertir Javascript en un lenguaje multiproceso y resolver este problema. Ahora, gracias a HTML5, podemos ejecutar códigos exigentes detrás de escena mientras el código principal sigue siendo ejecutado en la página web.
La forma en la que Web Workers trabaja es simple: el trabajador (worker) es construido en un archivo Javascript separado y los códigos son comunicados entre sí a través de mensajes. Normalmente, el mensaje enviado al trabajador (worker) desde el código principal es la información que queremos que sea procesada, mientras que los mensajes enviados en respuesta desde el trabajador representan el resultado de este procesamiento. Para enviar y recibir estos mensajes, la API aprovecha técnicas implementadas en otras API ya estudiadas. Eventos y métodos que ya conocemos son usados para enviar y recibir mensajes desde un código al otro. Los siguientes son los elementos provistos por la API con este propósito:
Worker(códigoURL) Antes de comunicarnos con el trabajador, debemos obtener un objeto que apunta al archivo que contiene el código del trabajador. Este método retorna un objeto Worker. El atributo códigoURL es la URL del archivo que contiene el código que será ejecutado detrás de escena.
postMessage(mensaje) Este método es el mismo estudiado antes en el Capítulo 13 para Web Messaging API, pero ahora implementado para el objeto Worker. El método envía un mensaje hacia o desde el código del trabajador. El atributo mensaje es una cadena de texto o un objeto JSON representando el mensaje a ser transmitido. message Este es un evento ya estudiado que escucha por mensajes enviados al código. Del mismo modo que el método postMessage (), este evento puede ser aplicado en el código principal o en el trabajador. Utiliza la propiedad data para obtener el mensaje enviado.
Para estudiar cómo los trabajadores y el código principal se comunican entre sí, vamos a usar una plantilla simple conteniendo un formulario donde ingresar nuestro nombre y una caja donde mostrar la respuesta recibida.
Todo ejemplo de Web Workers, incluso el más sencillo, requiere al menos tres archivos: el documento principal, el código Javascript principal, y el archivo con el código para el trabajador. El siguiente será nuestro documento HTML:
<!DOCTYPE html>
<html lang=»es»>
<head>
<title>WebWorkers</title>
<link rel=»stylesheet» href=»webworkers.css»>
<script src=»webworkers.js»></script>
</head>
<body>
<section id=»cajaformulario»>
<form name=»formulario»>
<p>Nombre:<br><input type=»text» name=»nombre»
id=»nombre»></p>
<p><input type=»button» name=»boton» id=»boton»
value=»Enviar»></p>
</form>
</section>
<section id=»caj adatos»></section>
</body>
</html>
Listado 14-1. Plantilla para probar Web Workers.
En la plantilla incluimos un archivo CSS llamado webworkers.css que contendrá las siguientes reglas:
#caj aformulario{
float: left;
padding: 20px;
border: 1px solid #999999;
}
#caj adatos{
float: left;
width: 500px;
margin-left: 20px;
padding: 20px;
border: 1px solid #999999;
}
Listado 14-2. Estilos para las cajas.
El código Javascript para el documento principal tiene que ser capaz de enviar la información que queremos procesar en el trabajador. También debe estar preparado para escuchar por respuestas.
function iniciar(){
caj adatos=document.getElementById(‘caj adatos’);
var boton=document.getElementById(‘boton’);
boton.addEventListener(‘click’, enviar, false);
trabaj ador=new Worker(‘trabaj ador.js’);
trabajador.addEventListener(‘message’, recibido, false);
}
function enviar(){
var nombre=document.getElementById(‘nombre’).value; trabajador.postMessage(nombre);
}
function recibido(e){
caj adatos.innerHTML=e.data;
}
window.addEventListener(‘load’, iniciar, false);
Listado 14-3. Un uso simple de la API.
El Listado 14-3 presenta el código para nuestro documento principal (el que se encuentra dentro del archivo webworkers.js). En la función iniciar(), luego de la creación de las necesarias referencias para el elemento cajadatos y el botón del formulario, el objeto Worker es construido. El constructor Worker() declara al archivo trabaj ador. j s como el contenedor del código para el trabajador y retorna un objeto Worker referenciando a este archivo. Todo proceso de interacción con este objeto será en realidad una interacción con el código de ese archivo.
Luego de la construcción de este objeto, agregamos una escucha para el evento message con la intensión de escuchar por mensajes provenientes del trabajador. Cuando un mensaje es recibido, la función recibido() es llamada y el valor de la propiedad data (el mensaje) es mostrado en pantalla.
La otra parte de la comunicación es realizada por la función enviar(). Cuando el usuario hace clic sobre el botón «Enviar», el valor del campo nombre es enviado como un mensaje hacia el trabajador usando postMessage ().
Con las funciones recibido () y enviar () a cargo de la comunicación, estamos listos para enviar mensajes al trabajador y procesar sus respuestas. Es momento de preparar el código para el trabajador:
addEventListener(‘message’, recibido, false);
function recibido(e){
var respuesta=’Su nombre es ‘+e.data; postMessage(respuesta);
}
Listado 14-4. Código para el trabajador (trabajador.js).
Del mismo modo que el código principal, el código para el trabajador tiene que escuchar constantemente por mensajes usando el evento message. La primera línea del código en el Listado 14-4 agrega una escucha para este evento. Esta escucha ejecutará la función recibido () cada vez que el evento es disparado (se recibe un mensaje desde el código principal). En esta función, el valor de la propiedad data es agregado a una cadena de texto predefinida y el resultado es enviado como respuesta usando nuevamente el método postMessage ().
Hágalo usted m,ismo: Compare los códigos de los Listados 14-3 y 14-4 (el código principal y el trabajador). Estudie cómo trabaja el procedimiento de comunicación y cómo los mismos métodos y eventos son aplicados con este propósito en cada uno de los códigos. Cree los archivos necesarios para probar este ejemplo usando los Listados 14-1, 14-2, 14-3 y 14-4, súbalos a su servidor y abra el documento HTML principal en su navegador.
Este trabajador es, por supuesto, extremadamente elemental. Nada es realmente procesado. El único proceso realizado es la construcción de una cadena de texto con el mensaje recibido y el envío de la misma de regreso como respuesta. Sin embargo, este ejemplo es útil para entender cómo los códigos se comunican entre sí y cómo podemos aprovechar esta API.
A pesar de su simplicidad, existen algunas cosas importantes que deberemos considerar antes de crear nuestros trabajadores. Los mensajes son la única forma de comunicarse directamente con los trabajadores. Estos mensajes, además, tienen que ser creados usando cadenas de texto u objetos JSON debido a que los trabajadores no pueden recibir otro tipo de datos. Tampoco pueden acceder al documento, manipular elementos HTML, y no tienen acceso a funciones y variables del código principal. Los trabajadores son como códigos enlatados, solo pueden acceder a información recibida a través de mensajes y enviar los resultados usando el mismo mecanismo.
A pesar de las limitaciones mencionadas, los trabajadores son flexibles y poderosos. Podemos usar funciones, métodos Javascript predefinidos y otras API desde el interior de un trabajador. Considerando la complejidad que estos códigos pueden alcanzar, la API Web Workers incorpora un evento específico para informar sobre errores y retornar toda la información posible acerca de la situación.
error Este evento es disparado por el objeto Worker en el código principal cada vez que ocurre un error en el trabajador. Usa tres propiedades para retornar información: message, filename y lineno. La propiedad message retorna el mensaje de error. Es una cadena de texto que nos informa que salió mal. La propiedad filename muestra el nombre del archivo con el código que causó el error. Esto es útil cuando archivos externos son cargados desde el trabajador, como veremos más adelante. Finalmente, la propiedad lineno retorna el número de línea en la cual el error ocurrió.
Veamos un ejemplo de un código que muestra errores generados por el trabajador:
function iniciar(){
caj adatos=document.getElementByld(‘caj adatos’);
var boton=document.getElementById(‘boton’);
boton.addEventListener(‘click’, enviar, false);
trabaj ador=new Worker(‘trabaj ador.js’);
trabajador.addEventListener(‘error’, errores, false);
}
function enviar(){
var nombre=document.getElementById(‘nombre’).value; trabajador.postMessage(nombre);
}
function errores(e){
cajadatos.innerHTML=’ERROR: ‘+e.message+'<br>’;
cajadatos.innerHTML+=’Archivo: ‘+e.filename+'<br>’;
caj adatos.innerHTML+=’Línea: ‘+e.lineno;
}
window.addEventListener(‘load’, iniciar, false);
Listado 14-5. Usando el evento error.
El último código presentado es similar al código principal del Listado 14-3. Construye el trabajador pero solo utiliza el evento error debido a que no queremos escuchar por mensajes desde el trabajador en este ejemplo, solo controlar errores. Es inútil, por supuesto, pero nos mostrará cómo los errores son retornados y qué clase de información es ofrecida en estas situaciones.
Para generar un error deliberadamente podemos llamar a una función no existente dentro del trabajador, como muestra el siguiente ejemplo:
addEventListener(‘message’, recibido, false);
function recibido(e){ prueba();
}
Listado 14-6. Un trabajador que no trabaja.
En el trabajador debemos usar el evento message para escuchar por mensajes provenientes del código principal, al igual que hicimos anteriormente, porque ésta es la forma en la que el proceso comienza. Cuando un mensaje es recibido, la función recibido () es ejecutada y la función no existente prueba () es llamada, generando de este modo un error.
Tan pronto como el error ocurre, el evento error es disparado en el código principal y la función errores () es llamada, mostrando en pantalla los valores de las tres propiedades provistas por el evento. Estudie el código del Listado 14-5 para entender cómo la función toma y procesa esta información.
Hágalo usted mismo: Para este ejemplo, usamos el documento HTML y las reglas CSS de los Listados 14-1 y 14-2. Copie el código del Listado 14-5 en el archivo webworkers.js y el código del Listado 14-6 en el archivo trabaj ador. js. Abra la plantilla del Listado 14-1 en su navegador y envié desde el formulario cualquier texto al trabajador. El error retornado por el trabajador será mostrado en pantalla.
Los trabajadores son unidades especiales de código que están constantemente trabajando detrás de escena, esperando por información para ser procesada. Los trabajadores serán, la mayoría de las veces, solo requeridos en circunstancias específicas y para propósitos especiales. Normalmente sus servicios no serán necesarios o requeridos todo el tiempo, por lo que será una buena práctica detenerlos o terminar sus procesos si ya no los necesitamos.
Con este objetivo, la API provee dos métodos diferentes:
terminate() Este método detiene el trabajador desde el código principal. close() Este método detiene el trabajador desde dentro del trabajador mismo.
Cuando un trabajador es detenido, todo proceso que aún se encuentra desarrollando es abortado y toda tarea en el bucle de eventos es descartada. Para probar ambos métodos, vamos a crear una pequeña aplicación que trabaja exactamente como nuestro primer ejemplo, pero también responde a dos comandos específicos: «cerrar1» y «cerrar2». Si las cadenas de texto «cerrarl» o «cerrar2» son enviadas desde el formulario, el trabajador será detenido por el código principal o el código del trabajador usando terminate () o close() respectivamente.
function iniciar(){
caj adatos=document.getElementByld(‘caj adatos’);
var boton=document.getElementById(‘boton’);
boton.addEventListener(‘click’, enviar, false);
trabaj ador=new Worker(‘trabaj ador.js’);
trabajador.addEventListener(‘message’, recibido, false);
}
function enviar(){
var nombre=document.getElementById(‘nombre’).value;
if(nombre==’cerrar1′){ trabaj ador.terminate();
caj adatos.innerHTML=’Trabaj ador Detenido’;
}else{
trabajador.postMessage(nombre);
}
}
function recibido(e){
caj adatos.innerHTML=e.data;
}
window.addEventListener(‘load’, iniciar, false);
Listado 14-7. Deteniendo el trabajador desde el código principal.
La única diferencia entre el nuevo código del Listado 14-7 y el del Listado 14-3 es la adición de un condicional if para controlar la inserción del comando «cerrarl». Si este comando es insertado en el formulario en lugar de un nombre, el método terminate () es ejecutado y un mensaje es mostrado en pantalla indicando que el trabajador fue detenido. Por el otro lado, si el valor es diferente al comando esperado, el mensaje es enviado al trabajador.
El código para el trabajador realizará una tarea similar. Si el mensaje recibido contiene la cadena de texto «cerrar2», el trabajador se detendrá a sí mismo usando el método close () o enviará una respuesta en caso contrario:
addEventListener(‘message’, recibido, false); function recibido(e){
if(e.data==’cerrar2′){
postMessage(‘Trabajador Detenido’);
close();
}else{
var respuesta=’Su nombre es ‘+e.data; postMessage(respuesta);
}
}
Listado 14-8. El trabajador se detiene a sí mismo.
Hágalo usted mismo: Use el mismo documento HTML y reglas CSS de los Listados 14-1 y 14-2. Copie el código del Listado 14-7 dentro del archivo webworkers. j s y el código del Listado 14-8 dentro del archivo trabajador.js. Abra la plantilla en su navegador y usando el formulario envíe el comando «cerrar1» o «cerrar2». Luego de ingresar uno de estos comandos el trabajador no responderá más.
Los trabajadores pueden presentar limitaciones a la hora de interactuar con el documento principal y acceder a sus elementos, pero cuando se trata de procesamiento y funcionalidad, como ya mencionamos, la situación mejora considerablemente. Por ejemplo, dentro de un trabajador podemos usar los métodos setTimeout () o setInterval (), cargar información adicional desde el servidor usando XMLHttpRequest e incluso utilizar algunas APIs para crear códigos profesionales. Esta última posibilidad es la más prometedora, pero tiene una trampa: deberemos aprender una implementación diferente para cada una de las APIs a implementar.
Cuando estudiamos algunas APIs, la implementación presentada en esos capítulos era la llamada asíncrona. Muchas APIs ofrecen una versión asíncrona y otra síncrona. Estas diferentes versiones de la misma API realizan las mismas tareas pero usando métodos específicos de acuerdo a la forma en que son procesadas.
Las APIs asíncronas son útiles cuando las operaciones a realizar consumen recursos y tiempo que afectan el normal funcionamiento del documento principal. Las operaciones asíncronas son realizadas detrás de escena mientras el código principal continúa procesándose sin interrupción. Los resultados son informados posteriormente a través de eventos. Debido a que los trabajadores son por naturaleza asíncronos (son procesados al mismo tiempo que el código principal) este tipo de operaciones ya no son necesarias, por lo que el código dentro de un trabajador se ejecuta de forma síncrona (incluyendo las APIs).
Hágalo usted mismo: No vamos a estudiar APIs síncronas en este libro. Varias APIs incluyen una versión síncrona, como API File o API IndexedDB, pero la mayoría de ellas están aún bajo desarrollo o son inestables en este momento. Para mayor información y ejemplos, visite nuestro sitio web y siga los enlaces correspondientes a este capítulo.
Algo que vale la pena mencionar es la posibilidad de cargar archivos JavaScript externos desde un trabajador. Un trabajador puede contener todo el código necesario para realizar cualquier tarea que se necesite, pero debido a que varios trabajadores pueden ser creados para el mismo documento, existe la posibilidad de que varias partes de este código se repita entre un trabajador y otro y se vuelva así redundante. Podemos seleccionar estos trozos de código y almacenarlos en un simple archivo que será luego cargado por cada trabajador usando el nuevo método importScripts ():
importScripts(archivo) Este método carga un archivo Javascript externo para incluir código
dentro de un trabajador. El atributo archivo indica la ruta del archivo a ser incluido.
Si alguna vez ha usado métodos en otros lenguajes de programación, seguramente habrá notado la similitud entre importScripts () y funciones como include () de PHP, por ejemplo. El código del archivo es incorporado dentro del trabajador y es ejecutado como si fuera parte de su propio código.
Para usar el nuevo método importScripts() necesitamos declararlo al comienzo del trabajador. El código del trabajador no estará listo para operar hasta que estos archivos sean completamente cargados.
importScripts(‘mascodigos.js’);
addEventListener(‘message’, recibido, false);
function recibido(e){ prueba();
}
Listado 14-9. Cargando códigos Javascript para el trabajador desde archivos externos.
El código en el Listado 14-9 no es funcional, es solo un ejemplo de cómo aplicar el método importScripts (). En esta hipotética situación, el archivo mascodigos.js conteniendo la función prueba () es cargado tan pronto como el trabajador es iniciado. A partir de este instante, la función prueba() (así como cualquier otra función dentro del archivo mascodigos .js) se encuentra disponible para el resto del código del trabajador.
Lo que hemos visto hasta el momento es llamado Dedicated Worker (Trabajador Dedicado). Este tipo de trabajador solo responde al código desde el cual fue creado. Existe otro tipo de trabajador llamado Shared Worker (Trabajador Compartido), el cual responde a múltiples documentos desde el mismo origen. Trabajar con múltiples conexiones significa que podemos compartir el mismo trabajador desde diferentes ventanas, pestañas o iframes, y podemos mantener a cada instancia actualizada y sincronizada para la construcción de aplicaciones complejas.
Las conexiones son hechas a través de puertos y estos puertos pueden ser almacenados en el trabajador para futuras referencias. Para trabajar con Trabajadores Compartidos y puertos, esta parte de la API incorpora nuevas propiedades, métodos y eventos:
SharedWorker(códigoURL) Este constructor reemplaza al constructor previo Worker() usado para Trabajadores Dedicados. Como siempre, el atributo códigoURL declara la ruta del archivo Javascript con el código para el trabajador. Un segundo atributo opcional puede ser agregado para especificar el nombre del trabajador. port Cuando el objeto SharedWorker es construido, un nuevo puerto es creado para este documento y asignado a la propiedad port. Esta propiedad será usada más adelante para referenciar el puerto y comunicarse con el trabajador. connect Este es un evento específico usado desde dentro del trabajador para controlar nuevas conexiones. El evento será disparado cada vez que un documento inicia una conexión con el trabajador. Es útil para seguir los pasos de todas las conexiones disponibles con el trabajador (para referenciar todos los documentos que lo están usando). start() Este método está disponible para los objetos MessagePort (uno de los objetos retornados durante la construcción del trabajador compartido) y su función es iniciar el envío de mensajes recibidos en un puerto. Luego de la construcción del objeto SharedWorker, este método debe ser llamado para iniciar la conexión.
El constructor sharedWorker() retorna un objeto SharedWorker y un objeto MessagePort con el valor del puerto a través del cual la conexión con el trabajador será hecha. La comunicación con el trabajador debe ser realizada por medio del puerto referenciado por la propiedad port.
Para estudiar el funcionamiento de los Trabajadores Compartidos, tendremos que usar al menos dos documentos diferentes ubicados en el mismo origen, un archivo Javascript para cada documento y un archivo más para el trabajador.
La plantilla para nuestro ejemplo incluye un iframe donde se cargará el segundo documento HTML. Ambos, el documento principal y el documento dentro del iframe, compartirán el mismo trabajador.
<!DOCTYPE html>
<html lang=»es»>
<head>
<title>WebWorkers</title>
<link rel=»stylesheet» href=»webworkers.css»>
<script src=»webworkers.js»></script>
</head>
<body>
<section id=»cajaformulario»>
<form name=»formulario»>
<p>Nombre:<br><input type=»text» name=»nombre»
id=»nombre»></p>
<p><input type=»button» name=»boton» id=»boton»
value=»Enviar»></p>
</form>
</section>
<section id=»cajadatos»>
<iframe id=»iframe» src=»iframe.html» width=»500″
height=»350″></iframe>
</section>
</body>
</html>
Listado 14-10. Plantilla para usar Trabajadores Compartidos.
El documento para el iframe es un simple documento HTML que incluye un elemento <section> para definir nuestra ya conocida cajadatos y el archivo iframe.js con el código necesario para realizar la conexión con el trabajador:
<!DOCTYPE html>
<html lang=»es»>
<head>
<title>iframe</title>
<script src=»iframe.j s»></script>
</head>
<body>
<section id=»caj adatos»></section>
</body>
</html>
Listado 14-11. Plantilla para el iframe (iframe.html).
Cada uno de los documentos HTML creados incluye su propio código Javascript para iniciar la conexión con el trabajador y procesar sus respuestas. Estos códigos deben construir el objeto SharedWorker y usar el puerto referenciado por el valor de la propiedad port para enviar y recibir mensajes. Veamos primero el código para el documento principal:
function iniciar(){
var boton=document.getElementById(‘boton’);
boton.addEventListener(‘click’, enviar, false);
trabaj ador=new SharedWorker(‘trabaj ador.js’);
trabajador.port.addEventListener(‘message’, recibido, false);
trabaj ador.port.start();
}
function recibido(e){ alert(e.data);
}
function enviar(){
var nombre=document.getElementByld(‘nombre’).value;
trabaj ador.port.postMessage(nombre);
}
window.addEventListener(‘load’, iniciar, false);
Listado 14-12. Conectando desde el documento principal (webworkers . j s).
Cada uno de los documentos que necesita utilizar el trabajador compartido deberá crear un objeto SharedWorker y establecer la conexión con el trabajador. En el código del Listado 14-12, el objeto es construido usando el archivo trabaj ador. j s como el archivo que contendrá al trabajador, y luego las operaciones de comunicación son hechas a través del puerto correspondiente usando la propiedad port.
Luego de que una escucha para el evento message es agregada para escuchar por respuestas provenientes del trabajador, el método start() es llamado para iniciar el proceso de envío de mensajes. La conexión con el trabajador compartido no es establecida hasta que este método es ejecutado (a menos que usemos manejadores de eventos, como onmessage, en lugar del método addEventListener ()).
La función enviar() es similar a ejemplos previos, excepto que esta vez la comunicación es realizada a través del valor de la propiedad port.
El código del iframe se asemeja al principal:
function iniciar(){
trabaj ador=new SharedWorker(‘trabaj ador.js’);
trabajador.port.addEventListener(‘message’, recibido, false);
trabaj ador.port.start();
}
function recibido(e){
var caj adatos=document.getElementById(‘caj adatos’);
caj adatos.innerHTML=e.data;
}
window.addEventListener(‘load’, iniciar, false);
Listado 14-13. Conectando desde el iframe (iframe. js).
En ambos códigos, el objeto SharedWorker es construido referenciando al mismo archivo (trabajador.js), y la conexión es establecida usando la propiedad port (aunque a través de puertos diferentes). La única diferencia entre el código para el documento principal y el código para el iframe es cómo la respuesta del trabajador será procesada. En el documento principal, la función recibido() muestra una ventana de alerta (ver Listado 14-12), mientras que dentro del iframe la respuesta es mostrada como un simple texto dentro del elemento cajadatos (ver Listado 14-13).
Es momento de ver cómo el trabajador compartido administra cada conexión y envía mensajes en respuesta al documento apropiado. Recuerde que solo existe un trabajador para ambos documentos (precisamente por esto se llama Trabajador Compartido). Debido a esto, cada solicitud de conexión recibida por el trabajador tiene que ser diferenciada y almacenada para futuras referencias. Vamos a grabar estas referencias a los puertos de cada documento en un array llamado puertos:
puertos=new Array();
addEventListener(‘connect’, conectar, false);
function conectar(e){
puertos.push(e.ports[0]); e.ports[0].onmessage=enviar;
}
function enviar(e){
for(f=0; f < puertos.length; f++){
puertos[f].postMessage(‘Su nombre es ‘+e.data);
}
}
Listado 14-14. Código para el trabajador compartido (trabajador.js).
El procedimiento es similar al usado para Trabajadores Dedicados. Esta vez solo tenemos que considerar a qué documento específico vamos a responder, porque varios pueden estar conectados con el trabajador al mismo tiempo. Con este propósito, el evento connect provee el array ports con el valor del nuevo puerto creado para la conexión que estamos estableciendo (el array contiene solo este valor localizado en el índice 0).
Cada vez que un código solicita una nueva conexión al trabajador, el evento connect es disparado. En el código del Listado 14-14, este evento llama a la función conectar (). En esta función realizamos dos operaciones: primero, el valor del puerto es tomado de la propiedad ports (índice 0) y almacenado en el array puertos (inicializado al comienzo del código del trabajador). Y segundo, el manejador de eventos onmessage es registrado para este puerto en particular y la función enviar () es declarada como la responsable de atender los mensajes recibidos.
Como resultado, cada vez que un mensaje es enviado hacia el trabajador desde el código principal, sin importar desde qué documento, la función enviar() en el trabajador es ejecutada. En esta función usamos un bucle for para extraer del array puertos todos los puertos abiertos para este trabajador y enviar un mensaje a cada documento conectado. El proceso es exactamente como en los Trabajadores Dedicados, pero esta vez varios documentos son respondidos en lugar de uno solo.
Hágalo usted mismo: Para probar este ejemplo, tendrá que crear varios archivos y subirlos al servidor. Cree un archivo HTML con la plantilla del Listado 14-10 y el nombre que desee. Esta plantilla usará el mismo archivo webworkers. css usado a lo largo del capítulo. Cree también el archivo webworkers. j s con el código del Listado 14-12, y el archivo iframe.html como la fuente del iframe con el código del Listado 14-11. También necesitará crear el archivo trabajador.js conteniendo el código del trabajador del Listado 14-14. Una vez que todos estos archivos son generados y subidos al servidor, abra el primer documento en su navegador. Use el formulario para enviar un mensaje al trabajador y ver cómo ambos documentos (el documento principal y el documento dentro del iframe) procesan la respuesta.
IMPORTANTE: En el momento de escribir estas líneas, los Trabajadores Compartidos solo funcionan en navegadores basados en el motor WebKit, como Google Chrome y Safari.
La API Web Workers incorpora la capacidad de multiprocesamiento a Javascript. Es la API que nos permite procesar códigos detrás de escena sin interrumpir el normal funcionamiento del código del documento principal.
Trabajadores
Dos clases diferentes de trabajadores son ofrecidos: Trabajadores Dedicados (Dedicated Workers) y Trabajadores Compartidos (Shared Workers). Ambos comparten los siguientes métodos y eventos:
postMessage(mensaje) Este método envía un mensaje al trabajador, el código principal o el puerto correspondiente. El atributo mensaje es la cadena de texto o el objeto JSON a ser enviado.
terminate() Este método detiene el trabajador desde el código principal. close() Este método detiene al trabajador desde dentro del trabajador. importScripts(archivo) Este método carga un archivo Javascript externo para incorporar código al trabajador. El atributo archivo indica la ruta del archivo a ser incluido. message Este evento es disparado cuando un mensaje es enviado al código. Puede ser usado en el trabajador para escuchar por mensajes provenientes del código principal o viceversa. error Este evento es disparado cuando ocurre un error en el trabajador. Es usado en el código principal para controlar por errores en el trabajador. Retorna información por medio de tres propiedades: message, filename y lineno. La propiedad message representa el mensaje de error, la propiedad filename muestra el nombre del archivo con el código que causó el error, y la propiedad lineno retorna el número de línea en la cual ocurrió el error.
Trabajadores dedicados (Dedicated Workers)
Los Trabajadores Dedicados tienen su propio constructor:
Worker(códigoURL) Este constructor retorna un objeto Worker. El atributo códigoURL es la ruta del archivo conteniendo el trabajador.
Trabajadores compartidos (Shared Workers)
Debido a la naturaleza de los Trabajadores Compartidos, la API ofrece algunos métodos,
propiedades y eventos específicos:
SharedWorker(códigoURL) Este constructor retorna un objeto SharedWorker. El atributo códigoURL es la ruta del archivo conteniendo el trabajador compartido. Un segundo atributo opcional puede ser especificado para declarar el nombre del trabajador.
port Esta propiedad retorna el valor del puerto de la conexión con el trabajador compartido.
connect Este evento es disparado en el trabajador compartido cuando una nueva conexión es solicitada desde un documento.
start() Este método inicia el envío de mensajes. Es usado para comenzar la conexión con el trabajador compartido.