Académique Documents
Professionnel Documents
Culture Documents
Definición
Ben Lesh a menudo define RxJs como "Lodash for asynchronous data" y ciertamente es así
pero cuando comenzamos a profundizar un poquito, rápidamente comenzamos a ver que
hay algo más. RxJs es una librería que toma lo mejor de patrones como el patrón observer,
el patrón iterador y paradigmas como la programación funcional como reza el eslogan
oficial. Esta diversidad de elementos hace que, en ocasiones, sea una librería con una curva
de aprendizaje un poco intimidatoria por el cambio que supone frente a otros paradigmas o
patrones más utilizados y con los que seguramente nos sentimos más familiarizados. A lo
largo de este post vamos a ver los distintos ingredientes que forman la receta de RxJs.
Stream
Una de las máximas en la programación reactiva es que "todo es un stream" por lo que con
RxJs, cualquier flujo de información será tratada como un stream. Eventos del ratón,
Arrays, rangos de números, promesas, etc. Todo será un stream.
//Observable
const myObservable$ = Rx.Observable.from([1,2,3]);
//Observer
const myObserver = {
next: x => console.log(`Observer next value: ${x}`),
error: err => console.error(`Observer error value: ${x}`),
complete: () => console.log(`Observer complete notification`),
};
myObservable$.subscribe(myObserver);
Como vemos el Observer es un objeto que dispone de 3 métodos para recibir información
acerca del Observable. Cada uno de estos métodos cumple una función y a través de cada
uno de ellos recibiremos distintos tipos de notificaciones del Observable.
arrayStream$
.subscribe(
next => console.log(next),
err => console.log(err),
() => console.log('completed!')
);
El resultado en ambos casos es el mismo, no hay ninguna diferencia, pero esta forma suele
ser algo más habitual ya que además no es necesario establecerlos todos.
Next
Rx.Observable.from([1,2,3])
.subscribe(next => console.log(next)); // -> 1, 2, 3
Error
Rx.Observable.from([1,2,3])
.subscribe(
next => console.log(next),
err => console.log(err) // Se ha producido un error
);
Complete
Complete es una notificación sin valor y se emite solo cuando el stream de datos ha
finalizado:
Rx.Observable.from([1,2,3])
.subscribe(
next => console.log(next),
err => console.log(err),
() => console.log('completed!') // Se ejecuta cuando finaliza el
stream, después del (3)
);
Es importante destacar que existen streams finitos y streams inifinitos, que nunca llegan a
emitir un complete. Un stream sobre un evento, click de ratón, movimiento del ratón o
similares, nunca llegaran a emitir un complete.
Estos 3 métodos han sido modificados en la versión 5 de RxJs para alinearlos con los
métodos de la propuesta de Observable para ECMAScript ya que en versiones anteriores
estos métodos eran: onNext() onComplete() y onError() y mucha de la documentación
existente hace referencia a ellos pero pertenecen a versiones anteriores de RxJs.
Pull vs Push
Dentro del patrón Observer existen diversas implementaciones donde la iniciativa sobre
quien comunica a quien puede variar. En un sistema basado en pull, será el consumidor de
la información (Observer) el que tome la iniciativa y le preguntará al productor de la
información (Observable) si existe nueva información del stream (Oye, ¿tienes nueva
información?). En un sistema basado en push será el productor (Observable) el que informe
al consumidor (Observer) (¡hey yo!, tengo nueva información), de tal forma que este se
limita a "reaccionar" a las notificaciones del Observable.
Otro de los patrones en los que se inspira RxJs, es el patrón iterador , que nos permite iterar
contenedores de información como, por ejemplo, un Array, sin exponer su representación
interna. Para ello se define un iterador -next-, que será el encargado de recorrer el
contenedor de información, manteniendo el cursor o índice con la posición del último valor
dado:
myStream$
.subscribe(x => console.log(`next ${ x }`));
La programación funcional, es el otro gran pilar sobre el que sea asienta RxJs y su
influencia es realmente grande. La programación funcional está de moda y eso que no es
especialmente nueva. Lenguajes como Haskell peina canas ya, pero como los pantalones de
pitillo, todo vuelve. Para hablar en extensión de la programación funcional harían falta
varios posts y esto queda lejos del objetivo de éste, pero si es importante destacar al menos
las características que más afectan a su uso en Rxjs:
Las funciones son ciudadanos de primera clase
Las funciones son las reinas del baile y permiten entre otras cosas el paso de funciones
como argumentos de otras funciones. En la programación funcional todo está orientado al
empleo de funciones.
Funciones puras
Son funciones que con los mismos argumentos siempre devolverán el mismo resultado.
Esto puede parecer una obviedad, pero si en una función dependemos de algún elemento
fuera de su ámbito o scope local, como variables globales o cosas así, la función deja de ser
pura ya que existen elementos externos que pueden variar su resultado. Otra característica
es que no tienen efectos colaterales (side effects) ya que no mutan datos, ni tienen acceso a
ningún elemento externo. Una llamada a un console.log en una función, técnicamente lo
convierte en un efecto colateral y por tanto dejaría de ser una función pura.
Las funciones de orden superior suelen ser muy utilizadas en la programación funcional.
Son funciones puras, que reciben otras funciones como argumentos para la realización de
cálculos. JavaScript está considerado un lenguaje funcional impuro ya que, si bien es un
lenguaje imperativo, si tiene algunos elementos de la programación funcional descritos más
arriba. Las funciones de orden superior como map, reduce o filter son un buen ejemplo de
ello.
Vamos a ver un ejemplo de empleo de este tipo de funciones con un Array de números de
los cuales queremos obtener la suma de todos los que sean pares: