ReactiveX

ReactiveX es un API que extiende del patrón observador para programación asíncrona que facilita el manejo de flujo de datos y eventos a través de la combinación del patrón Observer e Iterator.

Está disponible para los lenguajes:

  • Java: RxJava
  • JavaScript: RxJS
  • C#: Rx.NET
  • C#(Unity): UniRx
  • Scala: RxScala
  • Clojure: RxClojure
  • C++: RxCpp
  • Lua: RxLua
  • Ruby: Rx.rb
  • Python: RxPY
  • Go: RxGo
  • Groovy: RxGroovy
  • JRuby: RxJRuby
  • Kotlin: RxKotlin
  • Swift: RxSwift
  • PHP: RxPHP
  • Elixir: reaxive
  • Dart: RxDart

RxJS

RxJS (Por sus siglas en Inglés, "Reactive Extensions for JavaScript") es una librería de JavaScript para programación asíncrona y basada en eventos usando secuencias de observables. Proporciona tres tipos de elementos pricipales:

Observables
Representa la colección de futuros valores o eventos invocables.
Subscribers
Representa la ejecución de un observable recibiendo la información de este.
Operators
Son funciones inspiradas en los métodos de los arreglos tales como map, filter, concat, entre muchas otras.

Callbacks vs RxJS vs Promises

En los casos en los que se realizan solicitudes a un servidor, donde sabemos que la respuesta no va a ser inmediata, es necesario el uso de artefactos que nos permitan esperar la respuesta que deseamos sin recibir un time-out. En este caso se verá un rápido funcionamiento de los Callback, las Promesas y los Observers que propoprciona RxJS.

Callbacks

Simulando la petición a un servidor se realiza un timeOut de 2 segundos

							
const getUser = cb => {
	setTimeout(() => {
		cb({ saludo: 'Hola' })
	}, 2000)
}
	
getUser(msg => {
	console.log(msg.saludo) // Prints 'Hola' after 2 seconds
})
							
						

Esto funciona bien para casos sencillos como este, pero cuando se hace más complejo el manejo de errores, su compresión se hace más dificil.

							
const checkAuth = cb => {
	setTimeout(() => {
		cb({ isAuth: true })
	}, 2000)
}
const getUser = (authInfo, cb) => {
	if (!authInfo.isAuth) {
		cb(null)
		return
	}
	setTimeout(() => {
		cb({ saludo: 'Hola' })
	}, 2000)
}
checkAuth(authInfo => {
	getUser(authInfo, msg => {
		console.log(msg.saludo)
	})
})
							
						

Promises

Es cuando las promesas son útiles para realizar el manejo de errores y realizar un código de mejor comprensión

							
checkAuth()
	.then(authStatus => {
		return getUser(authStatus) // retorna una nueva promesa
	})
	.then(msg => {
		console.log(msg.saludo) // print "Hola"
	})
	.catch(error => {
		//Manejo del error
	})
							
						

Pero las promesas presentan la limitación de que solo pueden manejar una operación asíncrona por promesa y no es muy útil cuando la operación asíncrona no termina tras un valor. Es allí cuando los Observadores son tan útiles.

RxJS - Observables

Como ejemplo tomamos el caso de un botón cualquiera, el cual puede ser oprimido múltiples veces, de forma que el método subscribe() se suscribe a todos los eventos reconocidos por el Observable, reaccionando a cada uno de ellos. Adicionalmente se puede realizar el manejo de errores con facilidad desde el subscribe.

							
const button = document.querySelector('button')
const observable = Rx.Observable.fromEvent(button, 'click')
observable.subscribe(
	event => {
		console.log(event.target)
	},
	error => {
		console.log(error)
	}
)
							
						

Ventajas y desventajas de ReactiveX

Ventajas

Gran crecimiento
Es una herramienta funcional y poderosa, haciendo que su popularidad aumente y sea más atractiva y alcanzable.
Flexible
Es una herramienta que es compatible con gran cantidad de frameworks y lenguajes.
API de calidad
Proporciona una API elegante que facilita la descripción de flujos complejos, incluyendo un completo set de entidades que se encargan del trabajo pesado, ocultando la complejidad, lo que permite concentrarse en la lógica.
Facilidad de programación
Su amplia colección de operadores facilita la programación
Optimización y protección contra fugas de memoria
Muchos desarrolladores han participado en la optimización y pruebas de la herramienta resolviendo los principales problemas de memoria.
Pequeño y conciso
Su arquitectura modular lo hace pequeño, conteniendo solo las piezas necesarias.
Sin dependencias de terceros
No depende de ninguna librería de terceros, manteniedo su tamaño óptimo.
Comunidad grande
Tiene una gran comunidad diversa y receptiva, lo que permite encontrar ayuda para la solución de problemas con facilidad
Documentación
El aprendizaje de la herramienta no es el más sencillo, pero cuenta con una extensa documentación y una gran cantidad de recursos en su página oficial.
Actualizaciones periódicas
Cuenta con actualizaciones regulares, añadiendo constantemente mejoras y corrección de errores.

Desventajas

Requiere inmutabilidad de datos
La inmutabilidad de los datos no es un requisito estricto, pero al ser un concepto de la programación funcional y el paradigma reactivo, optimiza su funcionamiento.
Depuración y pruebas
Su depuración y pruebas es algo difícil para un ojo inexperto debido a algunas funciones específicas de la programación reactiva.
Problema estricto de tipeo
La única dependencia de RxJS es tslib, que proporciona compatibilidad con Typescript. Typescript proporciona un conjunto de ventajas, como escritura fuerte, autocompletar, etc., pero también proporciona un uso incorrecto de los modificadores de acceso (privado/público), lo que hace que los métodos internos sean accesibles desde el exterior.
Uso de Observables
Para el uso de Observables, todo el código debe estar bajo los Observables

Instalación de RxJS

La forma más sencilla de Instalación de RxJS es mediante el uso de npm a través del siguiente comando:

							
npm install rxjs
							
						

Importe

La manera preferida para realizar importes es la siguiente.

							
import {*functionalities*} from 'rxjs';
							
						

Por ejemplo:

							
import { of, map } from 'rxjs';

of(1, 2, 3).pipe(map((x) => x + '!!!')); // etc
							
						

O para importar el set completo de funcionalidades:

							
import * as rxjs from 'rxjs';

rxjs.of(1, 2, 3).pipe(rxjs.map((x) => x + '!!!')); // etc;
							
						

Características específicas

A continuación vamos a dar una serie de explicaciones de conceptos clave de la programación reactiva y de la librería RxJS



Programación Reactiva en pocas palabras

La programación reactiva es la programación orientada al manejo de Streams de datos asíncronos y la propagación de cambio



La librería ReactiveX en pocas palabras

Rx es una combinación de las mejores ideas del patrón observer y el patrón iterator



Stream de datos

Es uno o más datos que llegan de manera asincrónica. Por ejemplo: si miramos desde arriba una autopista, veremos pasar coches en una tasa variable; podemos decir que se trata de un stream de coches.

Una manera gráfica de mostrar lo que es un stream de datos es a través de un diagrama de canicas, los cuales tienen la siguiente notación:

Por ejemplo, con un flujo de datos que emite números del 0 al 3 tendríamos lo siguiente:



Patrón Iterator

Iterator es un patrón de diseño de comportamiento que te permite recorrer elementos de una colección sin exponer su representación subyacente (lista, pila, árbol, etc.).

La estructura del patrón iterator es la siguiente:

Ejemplo: código de Java que nos muestra el funcionamiento de este patrón para hacer la concatenación de un arreglo de Strings



Patrón Observador

Observer es un patrón de diseño de comportamiento que te permite definir un mecanismo de suscripción para notificar a varios objetos sobre cualquier evento que le suceda al objeto que están observando.

La imagen de arriba no solo nos muestra el funcionamiento del patrón observador, sino que nos da un analogía del mismo cuando se sigue una cuenta de twitter. Al suscribirse a una cuenta (observable), el seguidor (observador) va a ser notificado de los tweets nuevos de la cuenta y en cualquier momento puede romper esta suscripción, dejando de seguir la cuenta.



Pull VS Push

Pull y Push representan diferentes protocolos que describen cómo se relaciona el productor de datos con el consumidor.

Tipo Productor Consumidor
Pull Pasivo: Produce los datos cuando se le solicita Activo: Decide cuándo necesita los datos
Push Activo: Produce los datos a su propio ritmo Pasivo: Reacciona a los datos recibidos

Pull: En un sistema Pull, el consumidor determina cuándo quiere recibir datos del productor. El productor en sí mismo se despreocupa de cuándo se enviarán los datos al consumidor. Por ejemplo, todas las funciones JS son de tipo Pull; las funciones son productoras de datos y el código que las llama consume su valor de retorno.

Push: El productor determina cuándo enviar los datos al consumidor. Un ejemplo típico es un servidor de chat que envía los mensajes a medida que llegan a los usuarios.



Observadores Fríos VS Calientes

Frío: Los Observables "fríos" son aquellos que no emiten valores hasta que haya una suscripción activa, ya que la información es producida dentro del Observable y, por tanto, solo emiten valores en el momento en que se establece una nueva subscripción. Por eso, el ejemplo previo que hemos visto, math.random(), devuelve valores diferentes.

Caliente: Por contra, los Observables "calientes" son aquellos que pueden emitir valores sin que haya ninguna subscripción activa, porque la información del stream se produce fuera del propio Observable. RxJs dispone de algunos Observables ¨calientes¨ y el mejor ejemplo de estos es fromEvent, que nos permite establecer un Observable sobre cualquier tipo de evento (como el click del ratón):

Sesión práctica

Ejemplo 1

Ejemplo 2

Ejemplo 3

Operadores en RxJS

RxJS es principalmente útil por sus operadores. Aunque el Observable es la base, los operadores son las piezas esenciales que permiten que el código asincrónico complejo se componga fácilmente de manera declarativa.

Los Operadores canalizables

son del tipo que se pueden canalizar a Observables usando la sintaxis observableInstance.pipe(operator()). Estos incluyen filter(...), y mergeMap(...). Cuando se les llama, no cambian la instancia de Observable existente. En su lugar, devuelven un nuevo Observable, cuya lógica de suscripción se basa en el primer Observable.

Los operadores de creación

son el otro tipo de operador, los cuales se pueden llamar como funciones independientes para crear un nuevo Observable. Por ejemplo: of(1, 2, 3) crea un observable que emitirá 1, 2 y 3, uno tras otro.

Operador de creación: representación canica

Operador canalizable: representación canica

listado operadores