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.

Move y Merge en TFS

El equipo en el que trabajo está mejorando sus políticas de branching e intentando ser más riguroso a la hora de hacer Scrum.

Recientemente nos vimos en una situación que, aunque debería ser marginal, ocurre en todos los sprints (y probablemente en los de todos los equipos, para ser sinceros): un Product Backlog Item no se completó en el sprint.

Nuestra política de ramas es “un branch por característica”, por lo que tenemos tantos branches como PBIs. Además, para ser un poco más organizados, creamos carpetas padre para los PBIs que se hicieron en un sprint. Por eso, si el sprint es “Release 1 – Sprint 2”, tendremos una carpeta R1S2 que contendrá todos los branches correspondientes a los PBIs de ese sprint.

Solución 1: Llevar a Main

No podemos llevar el código inestable que hay en la rama original a Main y de ahí crear una nueva rama, puesto que estaríamos “rompiendo” Main. Además, tampoco tendría mucho sentido esta duplicidad.

Solución 2: Branch de Branch

Otra posible opción sería hacer un branch del branch original, en la nueva carpeta. Por ejemplo, hacer un branch de R1S2PBIXXX a R1S3PBIXXX, donde XXX es el ID del PBI no completado. El problema de esta solución es que, a la hora de llevar los cambios a Main, nos obligaría a ir hacia atrás en el tiempo, de la última rama a la anterior, y de ahí a Main. También nos obligaría a mantener “viva” la rama original, a la espera de recibir los cambios para poder, después, merguearse a Main y “morir”.

Solución 3: Move del Branch

Está creíamos que era la mejor solución: mover la rama de la carpeta original a la nueva. Por ejemplo, de R1S2 a R1S3. De este modo mantendríamos la rama “enganchada” a Main y podemos hacer el branch directamente desde la nueva posición.

Sin embargo, aquí viene TFS con las rebajas: al hacer el merge de vuelta a Main, interpreta TODOS los ficheros como mergeables, por haber sido movidos. Lo peor es que, si confirmamos el checkin, la historia de ese fichero refleja dicha entrada, aun cuando no hay diferencias reales en su contenido.

Parece ser que este comportamiento es premeditado y probablemente existan buenas razones para ello, pero a nosotros nos obliga a prescindir de esta solución, para no convertir el History de los ficheros en un pequeño caos.

Conclusiones

No existe una buena solución, pero tendremos que tirar con la Solución 2 porque es la única viable a día de hoy.

WebBrowser + Mango = IE9

El otro día me llegó al correo una pregunta que, como de costumbre, no supe responder. Me preguntaban si podíamos esperar que WebBrowser utilizara IE9 como navegador en Mango.

Lo primero que me sugería la lógica es que sí. WebBrowser había estado utilizando IE7 hasta ahora y, puesto que Mango trae como una de sus principales novedades IE9 como navegador, lo normal era esperar que fuera así.

Rebuscando por MSDN (ese sitio donde está toda la información pero es casi imposible encontrar nada), encontré un pequeño artículo sobre los cambios e incompatibilidades de Mango en el que, explícitamente, citaban a WebBrowser y decían lo siguiente (traducción de cosecha propia):

El control WebBrowser para Windows Phone se ha actualizado para soportar Internet Explorer 9. Las aplicaciones que usen este control deberían re-testearse para asegurar su compatibilidad con Internet Explorer 9.

Parecía suficiente, pero ya que tengo instalado Mango en el teléfono, decidí probarlo por mí mismo. Creé un proyecto en el que simplemente incrusté un WebBrowser y una caja de texto para meter una dirección. El resultado lo podéis en la siguientes imágenes, primero navegando a http://user-agent-string.info/parse para conseguir información sobre el User Agent String que está enviando el control WebBrowser.

MangoBrowser_7-31-2011_13.10.33.888

Y, en esta otra, navegando al test ACID3, que aunque no valga de mucho por ser muy poco exhaustivo, sí nos sirve para diferenciar entre IE7 e IE9.

MangoBrowser_ACID3_7-31-2011_13.14.50.684

En definitiva, WebBrowser en Mango SÍ funcionará sobre Internet Explorer 9.

Permisos a NetworkService en remoto

Hoy me he encontrado con una situación que, no siendo habitual, tampoco es tan extraña. Tenía una máquina con un servidor de aplicaciones IIS que hospedaba una aplicación web (unos servicios WCF, por concretar). El Application Pool sobre el que corría dicha aplicación web estaba utilizando la cuenta de Network Service de la máquina, y era preciso que accediera a una base de datos en otra máquina.

Parece fácil, ¿no? Pero, ¿cómo darle permisos a una cuenta que no está en nuestra máquina y que tampoco es una cuenta del dominio en el que se encuentran ambas máquinas? La primera pista la podemos encontrar en IIS.NET, en este post (en inglés). En él nos explican, entre otras muchas cosas, que cada cuenta Network Service de una máquina se convierte en una cuenta distinta cuando trata de acceder a recursos en máquinas distintas. Esta cuenta en la que se “transmuta” Network Service cumple el siguiente patrón:

<nombre_de_dominio><nombre_de_maquina>$

Con lo cual, si nuestra máquina XXX se encuentra en el dominio YYY, su cuenta “transmutada” sería: YYYXXX$. Fácil, ¿verdad? Ahora sólo haría falta dar permisos a dicha cuenta utilizando Management Studio, en el servidor de bases de datos al que queremos atacar. Sin embargo, aquí nos encontramos con otro pequeño problema: Management Studio no es capaz de reconocer dicha cuenta.

Por eso, en su lugar, tendremos que tirar de consola (o de una ventana de query del Management Studio) y ejecutar el siguiente procedimiento almacenado:

exec sp_grantlogin 'nombre_de_dominionombre_de_maquina$'

 

Y, ahora sí, refrescando la carpeta de Logins del servidor SQL Server, podremos a este nuevo login y configurarlo para que tenga los permisos necesarios para acceder a la base de datos que toca.

[WCF] Nombres de máquina incorrectos en WSDL

Nos había pasado anteriormente y nos ha vuelto a pasar: WCF nos generaba el documento WSDL con el nombre de la máquina, no con su dominio.

Esto hacía que, por ejemplo, una máquina con nombre XXX en un dominio YYY, aunque su WSDL estuviera accesible en http://XXX/Servicio.svc?wsdl, internamente las rutas del documento WSDL hicieran referencia a http://XXX.YYY/, lo que hacía el WSDL inservible.

Parece ser que, hasta WCF 4.0, la solución no era sencilla: había que parchear este "bug”, “feature” o como queramos llamarlo, tal como se explica en este KB de Microsoft: http://support.microsoft.com/kb/971842/en-us

Sin embargo, a partir de WCF se ha incorporado la corrección y ya podemos tirar directamente de XML y olvidarnos de DLLs. El fragmento sería tan sencillo como éste:

<behaviors>
  <serviceBehaviors>
    <behavior>
      <!-- Necessary to avoid problems with domain/computer names -->
      <useRequestHeadersForMetadataAddress>
        <defaultPorts>
          <add scheme="http" port="80" />
        </defaultPorts>
      </useRequestHeadersForMetadataAddress>
    </behavior>
  </serviceBehaviors>
</behaviors>

Con esto ya tendremos nuestros WSDLs enlazando internamente de forma correcta el resto de elementos de que se compone.