Por qué hacemos self = this en JS?

Cuando empecé a utilizar Knockout JS, descubrí que existía una cierta convención según la cual, al crear la función Constructor del modelo que se “bindea” con Knockout, la primera línea siempre era la siguiente:

var self = this;

No tenía ni idea de porqué se hacía esto, pero entendía que era necesario para, posteriormente, poder definir y añadir los distintos métodos y propiedades que formaban el modelo.

Ahora, gracias a los vídeos de “JavaScript The Good Parts” de Douglas Crockford en Pluralsight, por fin sé el porqué de esta misteriosa pero indispensable línea. Y las razones son realmente dos.

Una función anidada no tiene acceso al this externo

Como bien explica Douglas, cuando dentro de una función definimos otra función, la función anidada no tiene acceso al puntero this que la función externa ha recibido.

Para superar esta limitación, en ocasiones veremos código en el que se define una línea como la siguiente.

var that = this;

Con esto lo que conseguimos es capturar en la función superior el puntero this, para posteriormente permitir a la función anidada acceder a la variable that que contendrá el mismo valor que contenía this cuando fue capturado.

Exactamente la misma técnica es la que estaremos aplicando al capturar this en una variable de nombre self. De esta forma las distintas funciones que crearemos como parte de la definición del modelo, podrán acceder al this que originalmente recibió la función constructora.

Pero, por qué querríamos acceder a ese this original que la función constructora recibe? Aquí entra en juego el segundo principio que define esta técnica.

La función constructora recibe el nuevo objeto en this

Esta es la otra clave de esta técnica. Cuando una función se invoca con el operador new (como hacemos al crear el modelo), se crea un nuevo objeto y se asigna al puntero this que recibe la función que estamos invocando con el operador.

Esto a efectos prácticos significa que el this que recibiremos en la función constructora es el propio nuevo objeto al que estaremos añadiendo propiedades y métodos como parte del código de dicha función.

De este peculiar modo nuestra función constructora no sólo inicializa el objeto como haría un constructor de un lenguaje estático como Java o C#, sino que también añade la propia funcionalidad al asignar funciones y propiedades.

El Patrón Promise

El patrón Promise es precisamente nuevo, pero está creciendo en popularidad a raíz del gran uso que se hace de él en la API de WinJS, el wrapper JavaScript para hacer a la nueva API WinRT en Windows 8. El objetivo de este patrón es facilitar el modelado de procesos asíncronos, de forma que el código que consuma operaciones que no van a retornar inmediatamente sea lo más legible y fácil de escribir posible.

Escenario actual

La asincronía está de moda. La Nube, aplicaciones “responsivas”, etc. Todo parece empujar a los desarrolladores a hacer un uso cada vez mayor de este tipo de construcciones programáticas. Microsoft ha decidido hacer una apuesta decidida por la asincronía en las aplicaciones Metro con Javascript, por muchas razones.

En primer lugar hay que tomar en consideración también el hecho de que JavaScript sea un lenguaje de ejecución basado en un único hilo, lo que hace que cualquier tarea de larga duración congele la interfaz gráfica, con la consiguiente mala imagen para la aplicación frente al usuario. Además, la visión de Microsoft con respecto a las aplicaciones Metro se basa en fluidez, rapidez de respuesta y sensación de inmediatez, de ahí que la asincronía pase de ser una característica deseada a una obligación absoluta.

Por estas razones Microsoft ha decidido ofrecer versiones asíncronas de la gran mayoría de llamadas al sistema que WinRT pone a disposición de los desarrolladores. Estas llamadas van a implementar, en su gran mayoría, el patrón Promise, para simplificar su uso asíncrono.

Pero, ¿cómo es este patrón? ¿Qué características tiene y cómo podemos usarlo? Veámoslo.

El Patrón

Promises/A es el nombre del estándar detrás de este patrón. En él se propone que los objetos Promise tengan una propiedad then que será una función con tres parámetros:

  • Handler para gestionar el resultado satisfactorio.
  • Handler para gestionar los errores.
  • Handler para gestionar el progreso de la operación.

Al retornar un objeto promise la función estará “prometiendo” retornar un valor en algún punto del futuro próximo.

Esta implementación del estándar es exactamente la misma que ha hecho Microsoft en su librería. En el siguiente fragmento de código

myWebService.get("http://www.javierholguera.com")
    .then(
       function(result) { /* gestionar resultado correcto */},
        function(error) { /* manejar error */},
       function(progress) { /* informar sobre avances */}
     );

Para terminar uno de los principales beneficios del patrón es la facilidad para componer unos promises con otros. Por ejemplo, imaginemos que queremos realizar un proceso asíncrono A y cuando hayamos terminado dicho proceso satisfactoriamente, quedemos ejecutar un segundo proceso asíncrono B. Con el siguiente fragmento de código podemos ver cómo lo haríamos.

procesoA.ejecutar("http://www.javierholguera.com")
    .then(function(result) {
         // gestionamos el resultado de A ejecutando el proceso B
        // esta funcion retorna el promise del proceso B
        return procesoB.ejecutar(result);
    })
    .then(function() {
        // gestion del exito del proceso B
        console.log('B finalizo correctamente');
    }, function(error) {
        // gestion del error del proceso B 
        console.log('B finalizo con errores');
    });

Podemos apreciar en el código como el segundo .then está ejecutándose sobre el resultado retornado por el primer .then, que es en realidad el promise correspondiente al proceso B. En este caso he omitido, por claridad, el handler para el error en el promise del proceso A, pero podría haberse añadido perfectamente justo a continuación del manejador para el resultado exitoso del proceso A.

Conclusión

Promise es un patrón sencillo pero que nos ayuda a gestionar la asincronía de una forma fácil y limpia. Este patrón tiene una larga vida por delante, como parte fundamental de la API de WinJS en Windows 8, por lo que cuanto antes lo dominemos, antes empezaremos a escribir co´digo asíncrono como las aplicaciones Metro nos exigen.