Un pequeño paseo por ScalaRx: Parte II

Hace quince días estuvimos viendo muy por encima alguna de las funcionalidades que nos ofrecía la librería ScalaRx. Hoy vamos a acabar ese pequeño paseo dando un poco de caña a los Observables y todas las ventajas que nos ofrecen.

Anteriormente en Scalera …

En el post anterior  vimos como funcionaban las variables reactivas. Aprendimos como declararlas, como modificarlas y como crear estructuras Rx que fueran modificadas cuando las variables reactivas experimentaran un cambio.

La idea principal del post indicaba que el objetivo de la librería ScalaRx era interpretar las variables reactivas y su utilización como un flujo que propaga cambios cuando se produce alguna actualización en alguna de las variables reactivas. Podemos verlo como un efecto mariposa.

Ahora vamos a ver como podemos aprovecharnos de dichos cambios con el tipo Observable.

giphy

El tipo Observable

Mediante el tipo Observable podremos realizar determinadas acciones cuando una variable reactiva cambie de valor. Para ello podemos crear observaciones tanto de un Var como de bloques Rx. Existen dos constructores: mediante trigger o mediante foreach. Con el primero de ellos no tendremos una referencia al nuevo valor. Para obtenerlo debemos usar la llamada a .now. Por otro lado, con foreach tendremos una referencia al nuevo valor propagado en el flujo.

Vamos a crear un par de Observables que devuelvan por pantalla el nuevo valor. Para ello utilizaremos las dos versiones del constructor:

import rx._
val reactiveVar = Var("Hello World")
val o1 = reactiveVar.trigger {
  println(reactiveVar.now)
}
val o2 = reactiveVar.foreach { newValue =>
  println(newValue)
}

En este ejemplo, nada más ejecutarlo, mostrará por pantalla dos “Hello World” debido a que los observables actúan también en la inicialización de la variable reactiva. Si queremos saltarnos el primer cambio en la variable reactiva podemos usar triggerLater.

import rx._
val reactiveVar = Var("Hello World")
val o1 = reactiveVar.triggerLater {
  println(reactiveVar.now)
}

Por último, si queremos eliminar un Observable, podemos utilizar la instrucción kill.

import rx._
val reactiveVar = Var("Hello World")
val o1 = reactiveVar.triggerLater {
 println(reactiveVar.now)
}
o1.kill()

De esta manera, no se volverá a reaccionar a los cambios de la variable reactiva.

Conclusiones

Después de estos dos post, podemos comentar que ScalaRx se basa en flujos de datos en los que existen varios elementos. Como bien se comenta en la documentación de ScalaRx, dichos elementos se pueden ver como:

  • Var: inician los flujos y provocan una avalancha con sus cambios
  • Rx: nodos intermedios del flujo que reaccionaran a los cambios
  • Observables: nodos hoja del flujo que reaccionarán a los cambios sin propagar el flujo.

Podemos aprovecharnos de esta abstracción para crear distintos tipos de aplicaciones o herramientas. Por ejemplo, podríamos tener unos parámetros de configuración dinámicos (Var), que sean utilizados en otras variables (Rx) y que, si se producen cambios en ellos, necesitamos realizar una serie de acciones para adaptarnos a la nueva configuración:

import rx._
val host = Var(etcdHost)
val port = Var(etcdPort)
val databaseUri = Rx {host() + ":" + port()}
val obsDatabaseUri = databaseUri.triggerLater {
  connection.resetWith(databaseUri.now())
}

Este puede ser un patrón típico para adaptarnos a parámetros de configuración dinámica provenientes, por ejemplo, de ETCD o Consul.

Este es solo un ejemplo. Obviamente, podemos realizar cosas mucho más complejas con el flujo provocado por cambios en variables reactivas. Pero eso tocará verlo en futuros post. Agur de limón!

2 comentarios en “Un pequeño paseo por ScalaRx: Parte II

  1. he estado jugueteando, es una lástima que en el foreach no haya manera de acceder a un “oldValue”, por ejemplo, algo del tipo

    val a = Var(1)

    a.foreach(oldValue, newValue =>
    println(s”a ha variado su valor en ${newValue – oldValue}”)
    }

    a() = 12 //a ha variado su valor en 11
    a() = 5 //a ha variado su valor en -7

    etc

    por si a alguien le ayuda, he creado un gist con el código listo para ejecutarse y verse:

    view raw
    README.md
    hosted with ❤ by GitHub

    name := "rx-scalera-blog"
    version := "1.0"
    scalaVersion := "2.11.7"
    libraryDependencies += "com.lihaoyi" %% "scalarx" % "0.3.1"

    view raw
    build.sbt
    hosted with ❤ by GitHub

    ***************************************
    ScaleraBlog: Scala Rx Example, 1st Post
    ***************************************
    a vale 1
    b vale 2
    Lo guardamos en resultado, que es reactiva de a y b: 3
    Cambiamos el valor de b=6
    Resultado ahora debería valer 7
    7
    **************************************************
    ScaleraBlog: Scala Rx Example, 2nd Post. Example 1
    **************************************************
    Llamada a trigger:
    reactiveVar.now = Hello World
    Llamada a foreach:
    reactiveVar.now = [Hello World]
    newValue = [Hello World]
    **************************************************
    ScaleraBlog: Scala Rx Example, 2nd Post. Example 2
    **************************************************
    triggerLater no ejecuta en la inicialización de la variable. No deberías ver la llamada
    Pero foreach si que ejecuta
    Llamada a foreach:
    reactiveVar.now = [Hello World]
    newValue = [Hello World]
    Pero ahora cambio el valor de reactiveVar, deberia ejecutar triggerLater y foreach
    Llamada a foreach:
    reactiveVar.now = [Hello CRUEL world]
    newValue = [Hello CRUEL world]
    Llamada a triggerLater:
    reactiveVar.now = Hello CRUEL world
    Llamada a foreach:
    reactiveVar.now = [Hello CRUEL world]
    newValue = [Hello CRUEL world]
    Llamada a trigger:
    reactiveVar.now = Hello CRUEL world
    **************************************************
    ScaleraBlog: Scala Rx Example, 2nd Post. Example 3
    **************************************************
    Asignamos un callback foreach sobre rectiveVar
    Llamada a foreach:
    reactiveVar.now = [Hello CRUEL world]
    newValue = [Hello CRUEL world]
    Si ahora cambio el valor de reactiveVar, deberia ejecutar foreach
    Ahora ya no quiero escuchar cambios en la variable, por lo que si cambio el valor de
    reactiveVar, no se debe ejecutar ni foreach ni nada
    Llamada a foreach:
    reactiveVar.now = [Hello Biutiful world]
    newValue = [Hello Biutiful world]
    Llamada a triggerLater:
    reactiveVar.now = Hello Biutiful world
    Llamada a foreach:
    reactiveVar.now = [Hello Biutiful world]
    newValue = [Hello Biutiful world]
    Llamada a trigger:
    reactiveVar.now = Hello Biutiful world
    Ahora el valor e reactiveVar es… Hello Biutiful world

    view raw
    Output.txt
    hosted with ❤ by GitHub

    package com.mamoreno
    import rx._
    object ScalaRxExamples extends App {
    printHeader("ScaleraBlog: Scala Rx Example, 1st Post")
    ScaleraExamples.Post1.ex1()
    printHeader("ScaleraBlog: Scala Rx Example, 2nd Post. Example 1")
    ScaleraExamples.Post2.ex1()
    printHeader("ScaleraBlog: Scala Rx Example, 2nd Post. Example 2")
    ScaleraExamples.Post2.ex2()
    printHeader("ScaleraBlog: Scala Rx Example, 2nd Post. Example 3")
    ScaleraExamples.Post2.ex3()
    def printHeader(msg: String) {
    println("*" * msg.length)
    println(msg)
    println("*" * msg.length)
    }
    }
    object ScaleraExamples {
    import rx._
    object Post1 {
    def ex1()(implicit ctx: Ctx.Owner) = {
    val reactiveVar = Var(0)
    val a = Var(1)
    println (s"a vale ${a.now}")
    val b = Var(2)
    println (s"b vale ${b.now}")
    val resultado = Rx { a() + b() }
    assert(resultado.now == 3, "Algo ha ido mal con la reactividad")
    println(s"Lo guardamos en resultado, que es reactiva de a y b: ${resultado.now}")
    println("Cambiamos el valor de b=6")
    b()=6
    println("Resultado ahora debería valer 7")
    assert(resultado.now == 7, "Algo ha ido mal con la reactividad")
    println(resultado.now)
    }
    }
    object Post2 {
    val reactiveVar = Var("Hello World")
    def ex1()(implicit ctx: Ctx.Owner) = {
    val o1 = reactiveVar.trigger {
    println("Llamada a trigger:")
    println(s"\treactiveVar.now = ${reactiveVar.now}")
    }
    val o2 = reactiveVar.foreach { newValue =>
    println("Llamada a foreach:")
    println(s"\treactiveVar.now = [${reactiveVar.now}]")
    println(s"\tnewValue = [$newValue]")
    }
    }
    def ex2()(implicit ctx: Ctx.Owner) = {
    println("triggerLater no ejecuta en la inicialización de la variable. No deberías ver la llamada")
    val o1 = reactiveVar.triggerLater {
    println("Llamada a triggerLater:")
    println(s"\treactiveVar.now = ${reactiveVar.now}")
    }
    println("Pero foreach si que ejecuta")
    val o2 = reactiveVar.foreach { newValue =>
    println("Llamada a foreach:")
    println(s"\treactiveVar.now = [${reactiveVar.now}]")
    println(s"\tnewValue = [$newValue]")
    }
    println("Pero ahora cambio el valor de reactiveVar, deberia ejecutar triggerLater y foreach")
    reactiveVar() = "Hello CRUEL world"
    }
    def ex3()(implicit ctx: Ctx.Owner) = {
    println("Asignamos un callback foreach sobre rectiveVar")
    val o1 = reactiveVar.foreach { newValue =>
    println("Llamada a foreach:")
    println(s"\treactiveVar.now = [${reactiveVar.now}]")
    println(s"\tnewValue = [$newValue]")
    }
    println("Si ahora cambio el valor de reactiveVar, deberia ejecutar foreach")
    reactiveVar() = "Hello CRUEL world"
    println("""
    |Ahora ya no quiero escuchar cambios en la variable, por lo que si cambio el valor de
    |reactiveVar, no se debe ejecutar ni foreach ni nada
    """.stripMargin)
    o1.kill
    reactiveVar() = "Hello Biutiful world"
    println(s"Ahora el valor e reactiveVar es… ${reactiveVar.now}")
    }
    }
    }

    Le gusta a 1 persona

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s