jueves, 7 de febrero de 2013

HTML5: Introducción a los Web Workers 1


Uno de los mayores inconvenientes que tiene javascript como lenguaje de programación es que es un entorno de subproceso único. Esto implica que en procesos largos y costosos podemos dejar "congelado" el navegador del cliente.
Supongamos la siguiente página
<input id="txtValue" type="text" /><input id="btnProcess" onclick="_process()" type="button" value="Calculate primes" /><div id="output">
Y el siguiente código en javascript
function _process() {
    var value = document.querySelector('#txtValue').value;
    var primes = getPrimes(value);

    var output = document.querySelector('#output');
    output.innerHTML = primes.toString();
}

// Devuelve un array con los números primos comprendidos entre 1 y value.
function getPrimes(value) {
    var primes = [];
            
    for (var i = 1; i <= value; i++) {
        if (_isPrime(i))
            primes.push(i);                
    }

    return primes;
}

// Devuelve true si un número es primo, false en otro caso.
function isPrime(n) {
    if (n < 2) return false;
    var m = Math.sqrt(n);
    for (var i = 2; i <= m; i++)
        if (n % i == 0) return false;
    return true;
}
Si ponemos un valor elevado, por ejemplo un millón, vemos que durante un par de segundos nuestra página deja de responder.

Los Web Workers aparecen con la especificación de HTML5 y se definen como un script que se ejecuta en background independientemente de otros scripts y sin que afecte al rendimiento de la página.

Modificar el comportamiento del ejemplo anterior para que se ejecute en un Web Worker es bastante sencillo. La forma más fácil de hacer es aislar el código de nuestro Web Worker en un fichero y crear a partir de él nuestro objeto Worker. Para hacer esto haremos lo siguiente en nuestra página web
var worker = new Worker('webworker.js');
worker.addEventListener('message', _message, false);

function _message(e) {
    var output = document.querySelector('#output');
    output.innerHTML = e.data.toString();
}
En la primera linea creamos nuestro Worker a partir del código que tengamos en el fichero webworker.js, y en la segunda linea nos "enganchamos" al evento message del Worker, ya que a través de este evento es como se comunicará nuestro Worker con nosotros (la página web). Sólo nos falta saber como nos comunicaremos con nuestro Worker. Esto también es bastante sencillo y lo haremos de la siguiente manera
function _process() {           
    var value = document.querySelector('#txtValue').value;
    worker.postMessage(value);
}
Este método será llamado cada vez que pulsemos el botón "Calculate primes". En el extraemos el valor que queremos procesar y lo pasamos como parámetro en el método postMessage. Este método es la forma que tenemos de comunicarnos con el Worker.

Construir el Worker también es bastante fácil y en él se aplican los mismo métodos y eventos que hemos comentado anteriormente. El Worker recibe la información de la página a través del evento message y se comunicará con la página a través del método postMessage. Sabiendo esto el código del Worker quedaría de la siguiente forma
addEventListener('message', _message, false);

// Procesamos el mensaje recibido
function _message(e) {
    var primes = _getPrimes(e.data);
    self.postMessage(primes);
}

// Devuelve un array con los números primos comprendidos entre 1 y value.
function _getPrimes(value) {
    var primes = [];

    for (var i = 1; i <= value; i++) {
        if (_isPrime(i))
            primes.push(i);
    }

    return primes;
}

// Devuelve true si un número es primo, false en otro caso.
function _isPrime(n) {
    if (n < 2) return false;
    var m = Math.sqrt(n);
    for (var i = 2; i <= m; i++)
        if (n % i == 0) return false;
    return true;
}
Si ejecutamos este código con valores elevados, vemos que nuestra página no sufre ningún tipo de efecto secundario mejorando considerablemente la experiencia del usuario en la misma.

Otra cuestion a tener en cuenta en los Web Workers es a que tienen y a que no tienen acceso (por cuestiones de seguridad). Bien, los Web Wokers puede acceder a
  • Objeto navigator
  • Objeto location (de solo lectura)
  • XMLHttpRequest
  • setTimeout()/clearTimeout() y setInterval()/clearInterval()
  • Caché de la aplicación
  • Importación de secuencias de comandos externas a través del método importScripts()
  • Generación de otros Web Workers
Y no pueden acceder
  • DOM (no es seguro para el subproceso)
  • Objeto window
  • Objeto document
  • Objeto parent
Respecto a los navegadores compatibles, con IE solo podremos usar esta característica de HTML5 a partir de la versión 10. Para el resto de navegadores lo mejor es mirar en esta página.

Más información en la página de w3schools 

EDIT: Por recomendación @ydarias, añado la página de Mozilla sobre el uso de los web workers como lectura recomendada.

También se puede bajar el ejemplo que hemos comentado desde aquí.

Happy coding!

martes, 27 de noviembre de 2012

Limpiar un input file con jQuery

Hace bastante tiempo que no escribo nada porque ando bastante liado, pero me ha parecido bastante interesante compartir esto. El problema surge cuando tras subir un fichero a nuestro servidor queremos limpiar el contenido de nuestro input file desde cliente (por ejemplo, con jQuery). La primera aproximación podría ser esta
$('#fileupload').attr({ value: '' });
Si hemos trabajado algo con web sabemos que esta opción no es válida porque por motivos de seguridad el control es de solo lectura y no podemos modificar su contenido. La solución pasa por volver a crear el control usando el método clone de jQuery.
$fileupload = $('#fileupload');
$fileupload.replaceWith($fileupload.clone(true));
El método clone admite dos parámetros que nos permitirá conservar los valores y los manejadores de los eventos que tengamos definidos en nuestro control o controles hijos.

Espero que les sea útil. Happy coding!

viernes, 2 de noviembre de 2012

C# 5 - Caller Information

Una de las novedades que trae C# en su versión 5.0 es el Caller Information que nos puede ser bastante útil para trazar nuestras aplicaciones. Ahora podemos obtener la siguiente información del método "llamante"
  • CallerFilePathAttribute: Ruta completa del fichero que contiene al "llamante". Hay que tener en cuenta que es la ruta en el momento de la compilación.
  • CallerLineNumberAttribute: Número de linea que contiene la llamada a nuestro método. Line number in the source file at which the method is called.
  • CallerMemberNameAttribute: Método o propiedad del "llamante".
Y como siempre, lo mejor es verlo todo con un ejemplo.
private void button_Click(object sender, EventArgs e)
{
    //...
    NuestroMetodo();
    //...
}

public void NuestroMetodo([CallerFilePath] string sourceFile = "",
    [CallerLineNumber] int lineNumer = 0,
    [CallerMemberName] string memberName = "") {

    string msg = String.Format("{0}: Llamada a nuestro método desde {1}.{3}: line {2} en {4}",
        DateTime.Now.ToString("yyyy-mm-dd HH:MM:ss.fff"),
        Path.GetFileNameWithoutExtension(sourceFile),
        lineNumer,
        memberName,
        sourceFile);

    Debug.WriteLine(msg);
}
Y el resultado de ejecutar este código es el siguiente
2012-41-02 19:11:52.219: Llamada a nuestro método desde Form1.button_Click: line 25 en C:\Users\Sergio\Documents\Visual Studio 2012\Projects\CallerInformationDemo\CallerInformationDemo\Form1.cs
Hay que tener en cuenta que los parámetros a los que le apliquemos los atributos deben ser opcionales. Si no lo hacemos así, el compilador de C# nos los requerirá en la llamada, y los valores que pasemos sobreescribiran los valores por defecto.

No se hasta que punto es práctico decorar todos nuestro métodos con estos atributos pero quizás para determinados momentos nos pueda resultar útil guardar esta información para posteriormente analizarla.

Happy coding!

Más información sobre estos nuevos atributos en la MSDN.
CallerFilePathAttribute
CallerLineNumberAttribute
CallerMemberNameAttribute

domingo, 28 de octubre de 2012

Visual Studio 2012 - Cambiando el idioma del IDE


Para bien o para mal me he acostumbrado a utilizar el Visual Studio en inglés, así que cuando en la versión 2012 solo tenía acceso a la versión española me he sentido un poco perdido a la hora de navegar por los menús y demás opciones del IDE, menos mal que los atajos de teclado son los mismos.

Para cambiar el idioma del IDE tan sólo debemos descargar el Microsoft Visual Studio 2012 Language Pack en el idioma que queramos tener (en nuestro caso ingés) y posteriormente instalarlo. Luego iremos a Herramientas > Opciones y dentro del árbol vamos a Entorno > Configuración internacional y en idioma seleccionamos Inglés. Para que los cambios tengan efecto tendremos que cerrar el volver a abrir el Visual Studio.


Y así tenemos el antes


y el después


Y asunto resuelto, ya tenemos nuestro Visual Studio en inglés.

Happy coding!

sábado, 27 de octubre de 2012

Actualizando a Windows 8 Pro desde Windows 8 Release Preview

Desde ayer está disponible el nuevo Windows 8 para todos los públicos. Pese a que desde hace tiempo estoy usando la Release Preview ya era hora de actualizar el sistema, para entre otras cosas poder instalar el Visual Studio 2012 Pro (versión definitiva). En el momento de realizar la actualización me surgieron dos dudas
  1. Desde la página web donde podías comprar la actualización no decía nada de que se pudiera actualizar desde las versiones "betas" de Windows 8. Sólo se podía actualizar desde Windows XP SP3, Windows Vista y Windows 7.
  2. Si compraba la actualización, ¿qué era lo que realmente estaba comprando?¿una iso?¿una actualización  Y si mañana me compro un SSD y quiero hacer una instalación limpia, ¿puedo reinstalar mi Windows 8?
Con esta dudas lo primero que hice fue intentar acceder a la actualización a través de la propiedades del sistema. Pero tras varios intentos siempre obtenía el mismo error.


El siguiente pasó fue ir a la página de actualización y descargarme un ejecutable se encargaría del proceso de actualización. Así lo hice y de esta manera todo fue correcto. Nada más arrancar el "actualizador" éste se tomará su tiempo para analizar tu sistema actual e informarte de los problemas que puedes tener con la aplicaciones instaladas a la hora de actualizar tu sistema. En mi caso solo tendría problemas con el Daemon Tools Lite que uso para montar imágenes, o sea, un problema menor teniendo en cuenta que el Windows ya trae este comportamiento de serie. Tras esto podemos realizar la compra de la actualización





Tras realizar el pago se iniciará la descarga de la actualización. Una vez terminada la descarga nos preguntará que queremos hacer, si instalarla o por ejemplo crear un medio de instalación. En mi caso seleccione está opción, y cree una ISO que yo posteriormente quemé en un DVD y desde el que ahora mismo estoy realizando la instalación de Windows 8 sin problemas. Así que, pese a que estamos comprando una actualización tenemos una ISO que podemos guardar para posteriormente realizar otra instalación.


Notas
  • Desde esta página https://www.mswos.com/ podemos reiniciar la descarga introduciendo algunos datos como el número de pedido y el apellido y correo electrónico que usamos en la compra.
  • La nueva instalación me ha respetado completamente el arranque dual que tenía con Windows 7.

Happy coding!