Desarrollo de aplicaciones HTML5 para AppUp℠: Parte 2, Trabajadores web en aplicaciones.

 

Continuamos con el segundo de los capítulos de la serie de @jefftranter sobre cómo programar con HTML5 y cómo subir estas aplicaciones a AppUp.

Os recordamos que el primero de los capítulos está disponible en este enlace.

 

Introducción


 

En esta segunda entrega de nuestra serie sobre el desarrollo de aplicaciones HTML5 para AppUp, vamos a ver una nueva característica de HTML5, los trabajadores Web. Vamos a presentar a una aplicación que muestra el uso de Trabajadores de Web, así como algunas características de HTML5 tales como el canvas. Se concluye con un análisis de otras posibles aplicaciones de los Trabajadores del Web.

Todos los archivos de código fuente que se describen en este artículo pueden ser descargados desde aquí.

 

Trabajadores web


 

Normalmente, el código JavaScript que se ejecuta en un navegador se ejecuta en un solo hilo. Si necesita realizar algún tipo de cálculo intensivo de la CPU, esto puede ejecutarse desde el código JavaScript que se ejecuta con lentitud y que afecta a la capacidad de respuesta para el usuario. Pero podemos ser capaces de ejecutar código JavaScript en otro proceso o subproceso de fondo que resolver este problema. Además, las modernas CPU con varios núcleos, pueden ejecutar múltiples hilos en paralelo, ayudándonos a hacer uso de las capacidades del hardware.

Los trabajadores Web son una característica nueva de HTML5, soportando múltiples hilos de ejecución. Un Trabajador Web es un hilo que te permite ejecutar tareas de proceso en segundo plano, paralelamente con el subproceso de interfaz de usuario en el navegador principal. Un Trabajador Web es un archivo JavaScript que contiene la tarea que desea realizar en un hilo separado. Nos permite hacer, por ejemplo tareas intensivas de cálculo sin bloquear la interfaz de usuario.

Una aplicación común de estos elementos es la realización de una tarea costosa a nivel de cálculo, sin interrumpir la interfaz de usuario. También se puede utilizar para las operaciones que consumen tiempo, como operaciones de red.

Multithreading, por su propia naturaleza, plantea problemas potenciales de sincronización de datos compartidos que se acceden por varios subprocesos. Debido a esto, los trabajadores Web imponen algunas restricciones, el acceso sobre todo a la restricción DOM, el objeto ventana, objeto de documento, y el objeto principal de estos ejecutores web.

La aplicación de ejemplo con la que vamos a trabjar en este artículo, muestra el uso de los trabajadores Web para llevar a cabo una operación de uso intensivo de CPU, en concreto, el cálculo de números primos.

 

El Problema


 

Recordemos que un número primo es un número mayor que uno que no tiene divisores positivos distintos de uno y de sí mismo. Los números primos son importantes en muchas áreas de la computación, como el cifrado, pero para nuestros propósitos son un buen ejemplo de un cálculo intensivo de la CPU que puede ilustrar el uso de los trabajadores Web.

Un método sencillo para determinar si un número es primo es el conocido como sala de primera instancia. Consiste en comprobar si un número n dado es un múltiplo de cualquier número entero entre 2 y la raíz cuadrada de n. Si el resto después de dividir n por proceso es cero, entonces n no es primo. Hay algoritmos que son mucho más eficientes que la división de prueba, pero para nuestros propósitos de hacer un buen uso intensivo de la CPU pueden ser un problema.

En nuestro ejemplo de aplicación se calculan los números primos y se muestra los números primos encontrados recientemente. También vamos a mostrar el tiempo transcurrido desde que se iniciaron los cálculos. Como hay un número infinito de números primos, el programa nunca se completará. Nosotros le podemos ofrecer un botón Detener para detener los cálculos y un botón Cerrar para salir de la aplicación.

Para demostrar que los cálculos suceden en un hilo separado, vamos a mostrar una animación de un reloj con la función canvas en HTML5.

 

Descripción del código


 

Para empezar, necesitamos un archivo de icono estándar llamado icon.png para cumplir con las necesidades del Encapsulador.

Nosotros usaremos una hoja de estilo para aparentar el aspecto que queremos dar a los botones, la fuente del texto de los resultados, etc. Tú puedes revisar este archivo app.css cuando quieras.

La ejecución se inicia con el archivo index.html. Como se muestra en el Listado 1, se incluyen dos archivos JavaScript: stopwatch.js y main.js:

 

<!DOCTYPE HTML>
<html>
    <head>
        <title>Web Workers</title>
        <link href="app.css" rel="stylesheet" type="text/css" />
        <script src="stopwatch.js" type="text/javascript"></script>
        <script src="main.js" type="text/javascript"></script>
    </head>

Listado 1: archivo index.html, parte 1

 

Luego instalamos una imagen de fondo y llamamos a una función JavaScript para dibujar inicialmente el cronómetro, y luego exponer algunas variables que vamos a utilizar:

 

 

<body background="numbers.jpg" onload="stopwatch();">
        <script type="text/javascript">
            var running = false;
            var worker;
            var timerId = 0;
            var seconds = 0;
            var date;
    </script>

Listado 2: archivo index.html, parte 2

 

 

El resto del archivo muestra las cadenas de interfaz de usuario, define un canvas que vamos a utilizar para dibujar un cronómetro de animación, y define el inicio, parada, y los botones cerrar, relacionándolos con las funciones JavaScript para funcionar cuando se hace clic. Ten en cuenta que para el tiempo transcurrido y los resultados más altos de los números primos, usaremos etiquetas <output>. Ahora veremos la forma en que están conectados con el código que genera los resultados.

 

<h1>Web Workers Example: Prime Numbers</h1>
    <hr>
    <h3>Press the Start button to start the calculation process.<br>
        Press the Stop button to stop calculating.</h3>
    <canvas id="stopwatch" width=150 height=150>Your browser doesn't support HTML5 canvas</canvas>
    <h3>Elapsed time: <output id="elapsedTime"></output><br>
            Highest prime number found: <output id="primeNumber"></output></h3>
    
        <hr>
        <br>
        <a href="#" id="Start" class="button white" onclick="startWorker()">Start</a>
        <a href="#" id="Stop" class="button white" onclick="stopWorker()">Stop</a>
    <a href="#" id="Close" class="button white" onclick="closeApplication()">Close</a>
  </body>
</html>

Listado 3: archivo index.html, parte 3

 

 

Ahora vamos a ver el código de main.js, que se muestra en el Listado 4. Las funciones startWorker son llamadas cuando el botón Inicio es clicado. Si el subproceso de trabajo no se está ejecutando, se establece la variable de funcionamiento en tiempo real, el tiempo transcurrido a cero, y guarda la fecha y hora actuales. Se llama a init () de stopwatch.js que veremos más adelante.

A continuación creamos un nuevo trabajador, el trabajador de tipo Web JavaScript. Añadimos un detector de eventos para él, y configuramos el controlador de mensajes a la función e que veremos en breve. Hemos establecido los eventos para ir a la etiqueta de salida que se define en main.html utilizando su identificador primeNumber. Se obtiene la hora actual y se escribe en el campo de etiqueta de salida que hemos definido en main.html, utilizando su identificador elapsedTime. Por último, enviamos un mensaje a los trabajadores para ponerlos en marcha.

 

function startWorker()
{
    if (!running)
    {
        running = true;
        seconds = 0;
        date = new Date;
        init();

        worker = new Worker("worker.js");
        worker.addEventListener('message', onmessage, false);
        worker.onmessage = function(e) 
        {
            document.getElementById('primeNumber').textContent = event.data;
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(seconds - 1);
            document.getElementById('elapsedTime').textContent = date.toTimeString("hh:mm:ss").substring(0, 8);
        }
        worker.postMessage(); // Start worker
    }
}

function stopWorker()
{
    if (running)
    {
        running = false;
        clearInterval(timerId);
        worker.terminate(); // Stop worker
    }
}  

function closeApplication()
{
    intel.adp.encapsulator.closeapplication()
}

Listado 4: archivo main.js

 

Las funciones stopWorker se llaman cuando se hace clic en el botón Stop. Se establece la variable de funcionamiento en falso y se detiene un temporizador (que se creó cuando se llama a init (), como veremos más adelante). Después, termina el trabajador Web. Esto es importante ya que hay recursos que un trabajador utiliza que son liberados sólo cuando está terminado. La función closeApplication, llamada por el botón Close, simplemente cierra la aplicación.

Ahora echemos un vistazo al archivo worker.js, que se muestra en la lista 5. La función del controlador de mensajes en este archivo se ejecuta en el fondo de un hilo separado del hilo principal GUI JavaScript. Se busca números primos a partir de 2 utilizando el método de la división de juicio. Cuando un número primo se encuentra, se envía un mensaje que pasa el número como argumento, escribiéndose en el campo de salida establecido en la página web principal.

addEventListener('message', onmessage, false);

 

onmessage = function(e) 
{
    var n = 1;
    search: 
    while (true)
    {
        n += 1;
        for (var i = 2; i &lt;= Math.sqrt(n); i += 1)
            if (n % i == 0)
                continue search; // Number is not prime.
        // Number is prime, report it.
        postMessage(n);
    }
}

Listado 5: archivo worker.js

 

 

El archivo final es stopwatch.js, que se muestra en el Listado 6. La primera función es el archivo de inicio, que fue llamado desde startWorker. En primer lugar, llama a la función de cronómetro, y luego crea un temporizador que llama a la función del cronómetro cada segundo.

La otra función en el archivo, cronómetro, dibuja un reloj animado con una aguja que indica los segundos. Se utiliza el elemento canvas creado anteriormente para dibujar. La ubicación de la manecilla del reloj se dibuja con los valores de segundos calculados anteriormente sobre la base de la hora actual. El código es un poco largo, pero es sencillo. Puedes revisarlo, si te apetece.

 

function init()
{
    stopwatch();
    timerId = setInterval(stopwatch, 1000);
}

function stopwatch()
{
    var elem = document.getElementById('stopwatch');

    if (elem &amp;&amp; elem.getContext)
    {
        var context = elem.getContext('2d');

        if (context)
        {
            context.save();
            context.clearRect(0, 0, 150, 150);
            context.translate(75, 75);
            context.scale(0.5, 0.5);
            context.rotate(-Math.PI / 2);
            context.strokeStyle = "grey";
            context.fillStyle = "white";
            context.lineWidth = 6;
            context.lineCap = "square";

            context.save();
            context.lineWidth = 5;
            for (i = 0; i &lt; 60; i++)
            {
                if (i % 5 != 0)
                {
                    context.beginPath();
                    context.moveTo(120, 0);
                    context.lineTo(115, 0);
                    context.stroke();
                }

                context.rotate(Math.PI / 30);
            }

            context.restore();

            context.save();
            for (var i = 0; i &lt; 12; i++)
            {
                context.beginPath();
                context.rotate(Math.PI / 6);
                context.moveTo(120, 0);
                context.lineTo(100, 0);
                context.stroke();
            }

            context.restore();
            context.fillStyle = "black";

            context.save();
            context.rotate(seconds++ * Math.PI / 30);
            context.lineWidth = 6;
            context.lineCap = "round";
            context.strokeStyle = "red";
            context.beginPath();
            context.moveTo(-30, 0);
            context.lineTo(100, 0);
            context.stroke();
            context.beginPath();
            context.arc(0, 0, 10, 0, 2 * Math.PI, false);
            context.fill();
            context.restore();

            context.beginPath();
            context.strokeStyle = "#417FA1";
            context.lineWidth = 10;
            context.arc(0, 0, 140, 0, 2*Math.PI, false);
            context.stroke();
            context.restore();
        }
    }
}


Listado 6: archivo stopwatch.js

 

La figura 1 muestra una captura de pantalla de la aplicación ejecutándose:

 

Como hay un número infinito de números primos, el algoritmo nunca se completará. Nuestro programa de ejemplo fallará en el futuro cuando los valores de los números que utiliza consiga resultados tan grandes que la representación de JavaScript de este valor devolvería siempre el mismo. Esto sucedería después de varias horas de ejecución.

 

Otras aplicaciones


 

Para una aplicación de uso intensivo de CPU, puedes pensar que el uso de un lenguaje interpretado como JavaScript puede ser lento, pero los resultados pueden ser sorprendentemente buenos con los modernos motores de JavaScript. Como punto de referencia muy simple, que midió el tiempo que necesitó la aplicación de los Trabajadores para el cálculo de los números primos hasta 10 millones. Me compararon con un nativo de C + + que utiliza el mismo algoritmo para calcular números primos. La aplicación HTML5 tomó cerca de dos minutos para el cálculo de los números primos entre 1 y 10 millones. El programa en C + + tomó unos 35 segundos en el mismo hardware. Era una comparación un tanto injusto porque el programa HTML5 también es necesario para realizar la salida de los resultados en un formato amigable. Aun así, el código JavaScript sigue en el mismo orden de magnitud de actuación como el código nativo.

Si necesitas optimizar el rendimiento con código nativo, una alternativa es una aplicación híbrida que tiene tanto navegador (HTML5) como el código nativo. Esto, por supuesto, va a sacrificar la portabilidad.

Algunas otras áreas donde los trabajadores Web pueden ser útil son los siguientes:

  • La realización de red de entrada / salida en el fondo
  • Texto enriquecido (código fuente e.g) resaltado de sintaxis
  • Procesamiento de imágenes de datos extraídos de los elementos <canvas> o <video>
  • Actualización de una base de datos del lado del cliente

 

Conlusiones


En este artículo presentamos la función Web HTML de los trabajadores. Mostramos un ejemplo de aplicación sencilla con estos y discutido un poco más de manera realista sobre ejemplos en los que se podría trabajar.

 

Copyright © 2011, Intel Corporation.

 

Enlace original disponible aquí.

Теги:
Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.