RxJS: 2. Operadores I

Operadores de RxJS (primera parte)

Vangel Naumovski

archivado en: JavaScript / 22 marzo, 2018 / taller:

En la entrada anterior de esta serie comenzamos a ver qué era la programación reactiva, la librería RxJS y los dos actores principales de esta nueva manera de trabajar: los observables y los observers. En esta vamos a ver con más detalle algo que apenas enuncié en la anterior, los operadores

Los operadores

El verdadero potencial de RxJS se alcanza cuando comenzamos a usar operadores (operators), que son métodos que se aplican a los observables para modificar su naturaleza o su respuesta mediante la creación de un nuevo observable.  Para entenderlo podemos pensar en un stream observable que sea un periódico y en un observer, el lector, que solo está interesado por las noticias de internacional. Para facilitarle la lectura, podríamos entonces aplicar un operador al stream que dejara solo las noticias que le interesan, las cuales formarían un nuevo periódico, un nuevo observable, que no modifica el original.

miPeriodico = Rx.Observable.create((observer)=> {

observer.next('todas las noticias');

}).filter(

// dejamos solo las internaciones

);

Hay una montonera, así que me limitaré en esta entrada y en la siguiente a ver unos pocos a modo de ejemplo.

A. Creación de observables (Creation Operators)

Son operadores que sirven para crear observables y son:

  • ajax
  • bindCallback
  • bindNodeCallback
  • create
  • defer
  • empty
  • from
  • fromEvent
  • fromEventPattern
  • fromPromise
  • generate
  • interval
  • never
  • of
  • repeat
  • repeatWhen
  • range
  • throw
  • timer

Remito al lector interesado a la documentación oficial para ver todos en detalle, pero a modo de ejemplo, podemos destacar los siguientes:

1. create. Como vimos en la entrada anterior, crea un observable que recibe una función callback donde definimos el next, el error y el complete.

let myObservable = Rx.Observable.create((observer)=> {

observer.next('foo');

});

myObservable.subscribe(

(value)=> {

console.log(value);

}

);

// foo

2. from. Es casi un comodín, crea un observable a partir de un array, un objeto iterable, una promesa...

let myArray = [1, 2, 3];

myObservable = Rx.Observable.from(myArray);

myObservable.subscribe(

(value)=> {

console.log(value);

}

);

// 1

// 2

// 3

3. fromEvent. Genera un observable a partir de un evento, como un click o un mouse over. Recibe dos parámetros, el primero el nodo que tiene el evento y el segundo el tipo de evento.

let myButton = document.getElementById('myButton');

myObservable = Rx.Observable.fromEvent(myButton, 'click');

// MouseEvent {isTrusted: true,...

4. fromPromise. Como indica el nombre, genera un observable a partir de una promesa y lo que hace es emitir el resolve o el error.

myObservable = Rx.Observable.fromPromise(fetch('https://restcountries.eu/rest/v2/all'));

// Response {type: "cors", url: "https://restcountries.eu/rest/v2/all", ...

5. of. Crea un observable a partir de un conjunto de datos, que puede ser homogéneo o no, los cuales va devolviendo de forma sucesiva.

myObservable = Rx.Observable.of(1, 'foo', ['a', 'b']);

// 1

// foo

// ['a', 'b']

6. ajax. Es de los más útiles y lo que hace es devolver la respuesta de una llamada ajax. Puede ser muy simple, un mero get sin más parámetros.

let  myObservable = Rx.Observable.ajax('https://restcountries.eu/rest/v2/all');

// {originalEvent: Event, xhr: XMLHttpRequest, request: ...

O más complejo:

myObservable = Rx.Observable.ajax({

url: 'https://restcountries.eu/add',

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: {

country: 'Myanmar',

}

});

B. Filtros (Filtering Operators)

Este conjunto de operadores agrupa aquellos que sirven para filtrar la respuesta, la emisión de datos, de un observable antes de que le llegue al observer y también son un montón.

  • debounce
  • debounceTime
  • distinct
  • distinctKey
  • distinctUntilChanged
  • distinctUntilKeyChanged
  • elementAt
  • filter
  • first
  • ignoreElements
  • audit
  • auditTime
  • last
  • sample
  • sampleTime
  • single
  • skip
  • skipUntil
  • skipWhile
  • take
  • takeLast
  • takeUntil
  • takeWhile
  • throttle
  • throttleTime

Veamos algunos ejemplos para entender cómo funcionan estos operadores, pero como ya nos hemos hecho una idea general, usaré una fórmula más acotada, sin cachear el observable y concatenando directamente el observer.

1. distinct. Este operador, que es muy útil, devuelve aquellos elementos que no se repiten respecto a los que ya se han devuelto.

let myArray = [1, 3, 5, 6, 8, 8, 10, 1, 5];

Rx.Observable

.from(myArray)

.distinct()

.subscribe(

(value)=> {

console.log(value);

}

);

// 1

// 3

// 5

// 6

// 8

// 10

2. filter. Este operador es muy parecido al método homónimo de javaScript para trabajar con arrays y lo que hace es filtrar los resultados para que solo se devuelvan los que cumplen la condición indicada.

Rx.Observable

.from(myArray)

.filter(item => {

return item < 5;

}).subscribe(

(value)=> {

console.log(value);

}

);

// 1

// 3

// 1

3. first. Devuelve el primer elemento de una colección.

Rx.Observable

.from(myArray)

.first();

// 1

4. skip. Omite los n primeros elementos indicados en el parámetro de entrada.

Rx.Observable

.from(myArray)

.skip(5);

// 8

// 10

// 1

// 5

5. throttle. Aunque es más complejo que el tono que quería mantener en esta primera inmersión en los operadores, valga para terminar este grupo uno muy útil llamado throttle, el cual detiene la ejecución de un observer durante el tiempo indicado en un segundo observer. Sirve, por ejemplo, para colocar el típico flag anti-histéricos que evita que se lance un evento tropecientas mil veces por haber sido pulsado un botón de forma compulsiva.

let myButton = document.getElementById('myButton');

myObservable = Rx.Observable.fromEvent(myButton, 'click')

.throttle(ev => Rx.Observable.interval(1000))

.subscribe(ev => console.log(ev));

// el evento solo se lanza cada 1000 ms.

C. Condicionales

Por la naturaleza un tanto heterogénea de estos operadores, el nombre que le han dado a este grupo en la documentación oficial es un poco extraño, conditional and boolean operators, pero quizás habría sido mejor llamarlo operadores formidables, ya que cada uno es más útil que el anterior.

  • defaultIfEmpty
  • every
  • find
  • findIndex
  • isEmpty
 

Vamos a verlos todos.

1. defaultIfEmpty . Devuelve un valor por defecto si la fuente de datos del observable está vacía.

let myArray = [];

Rx.Observable

.from(myArray)

.defaultIfEmpty('no data oO')

.subscribe(value => console.log(value));

// no data oO

2.  isEmpty. Devuelve true o false según la fuente de datos esté vacía o no.

Rx.Observable

.from(myArray)

.isEmpty()

.subscribe(check => console.log(check));

// true

3. every. Devuelve true o false si al menos uno de los elementos cumple la condición indicada.

myArray = [1, 2, 3];

Rx.Observable

.from(myArray)

.every(x => x > 5)

.subscribe(condition => console.log(condition));

// false

4. find. Este operador, que es mi favorito, se parece al find de lodash. Devuelve el primer ítem de un conjunto iterable que se ajusta a la condición indicada.

myObject = [{key: 1, name: 'foo'}, {key: 2, name: 'foo'}];

Rx.Observable

.from(myObject)

.find(item => item.key === 2)

.subscribe(value => console.log(value.name));

// foo

5. findIndex. Se parece mucho al anterior, pero en este caso se devuelve solo el índice de la colección.

myObject = [{key: 1, name: 'foo'}, {key: 2, name: 'foo'}];

Rx.Observable

.from(myObject)

.findIndex(item => item.key === 2)

.subscribe(index => console.log(index));

// 1

D. Matemáticos (mathematicals)

Aquí tenemos los operadores que permiten realizar operaciones matemáticas básicas sobre un conjunto de datos numéricos. Son pocos.

  • count
  • max
  • min
  • reduce

1. count. Devuelve el número de items que cumplen una condición.

let myArray = [1, 2, 3, 9];

Rx.Observable

.from(myArray)

.count(item => item > 5)

.subscribe(count => console.log(count));

// 1

2. max. Devuelve el ítem más grande de la colección. Si es un array sencillo, evalúa cada dato en sí mismo, de lo contrario hay que definir la condición (como sucede con el sort de javaScript).

myArray = [1, 2, 3, 9];

Rx.Observable

.from(myArray)

.max()

.subscribe(maxValue => console.log(maxValue));

// 9

3. min. Igual que el caso anterior, pero el valor menor.

myObject = [{value: 1, name: 'foo'}, {value: 2, name: 'bar'}];

Rx.Observable

.from(myObject)

.min((a, b) => a.value < b.value ? -1 : 1)

.subscribe(minValue => console.log(minValue.name));

// foo

4. reduce. Funciona igual que el reduce() de javaScript. Devuelve un solo valor como resultado de una operación entre todos los elementos de la colección.

myArray = [1, 2, 3, 9];

Rx.Observable

.from(myArray)

.reduce((prev, next)=> prev + next)

.subscribe(total => console.log(total));

// 15

Bueno, quedan más grupos por ver, pero como esta entrada ya ha quedado kilométrica, los vemos en la próxima.

|| Tags:

Este artículo aún no ha sido valorado.

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.