¿Para qué sirve la tubería en rxJS?

104

Creo que tengo el concepto base, pero hay algunas oscuridades

Entonces, en general, así es como uso un observable:

observable.subscribe(x => {

})

Si quiero filtrar datos, puedo usar esto:

import { first, last, map, reduce, find, skipWhile } from 'rxjs/operators';
observable.pipe(
    map(x => {return x}),
    first()
    ).subscribe(x => {

})

También puedo hacer esto:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';

observable.map(x => {return x}).first().subscribe(x => {

})

Entonces mis preguntas son:

  1. ¿Cuál es la diferencia?
  2. Si no hay diferencia, ¿por qué existe la tubería de función?
  3. ¿Por qué esas funciones necesitan diferentes importaciones?
enno.void
fuente
1
Estuve a punto de decir que es para operadores personalizados, no nativos, pero ni siquiera sé si eso es correcto. ¿ pipe()Te permite pasar los operadores que creas?
zero298

Respuestas:

69

Los operadores "pipables" (antes "permitibles") son la forma actual y recomendada de usar operadores desde RxJS 5.5.

Te recomiendo encarecidamente que leas la documentación oficial https://rxjs.dev/guide/v6/pipeable-operators

La principal diferencia es que es más fácil hacer operadores personalizados y que es mejor intercambiables sin alterar algún Observableobjeto global que podría hacer posibles colisiones si dos partes diferentes quisieran crear un operador con el mismo nombre.

Usar importdeclaraciones separadas para cada operador 'rxjs/add/operator/first'fue una forma de hacer paquetes de aplicaciones más pequeños. Al importar solo los operadores que necesita en lugar de toda la biblioteca RxJS, puede reducir significativamente el tamaño total del paquete. Sin embargo, el compilador no puede saber si lo importó 'rxjs/add/operator/first'porque realmente lo necesita en su código o simplemente se olvidó de eliminarlo al refactorizar su código. Esa es una de las ventajas de utilizar operadores pipables donde las importaciones no utilizadas se ignoran automáticamente.

martín
fuente
1
Sobre su afirmación unused imports are ignored automatically, actualmente los IDE tienen complementos que eliminan las importaciones no utilizadas.
silvanasono
No todo el mundo utiliza estos IDE o estos complementos, muchas personas utilizan un editor de texto básico. Probablemente la mayoría de las veces no podemos confiar en la afirmación de que todos los miembros del equipo están usando el mismo IDE / conjunto de complementos / editor de texto que nosotros.
Adam Faryna
3
@AdamFaryna seguro, algunos equipos también pueden escribir código en papel, pero ¿por qué lo harían si tienen herramientas modernas disponibles? Usar un editor de texto, especialmente sin los complementos importantes, es similar a escribir código en papel. Puede hacer eso, pero ¿por qué un equipo / desarrollador decente haría eso
Denes Papp
El editor de código @DenesPapp no ​​importa siempre que la gente pueda usarlo de manera productiva. Aparte de eso, son solo preferencias personales. Su analogía con escribir código en papel es inexacta, no puede ejecutar código en papel, pero el código escrito en cualquier editor de texto se puede ejecutar.
Adam Faryna
1
@perymimon Puedes, pero tienes que instalar el rxjs-compatpaquete github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
martin
16

El método de la tubería

Según documentación original

el operador pipable es que la función toma observables como entrada y devuelve otro observable. El observable anterior permanece sin modificar.

pipe(...fns: UnaryFunction<any, any>[]): UnaryFunction<any, any>

Publicación original

¿Qué significa pipa?

Eso significa que cualquier operador que haya utilizado anteriormente en la instancia de observable está disponible como funciones puras en rxjs/operators. Esto hace que la creación de una composición de operadores o la reutilización de operadores se vuelva realmente fácil, sin tener que recurrir a todo tipo de gimnasia de programación en la que debe crear un Observable extensible observable personalizado y luego sobrescribir el levantamiento solo para hacer su propia cosa personalizada.

const { Observable } = require('rxjs/Rx')
const { filter, map, reduce,  } = require('rxjs/operators')
const { pipe } = require('rxjs/Rx')

const filterOutWithEvens = filter(x => x % 2)
const doubleByValue = x => map(value => value * x);
const sumValue = reduce((acc, next) => acc + next, 0);
const source$ = Observable.range(0, 10)

source$.pipe(
  filterOutWithEvens, 
  doubleByValue(2), 
  sumValue)
  .subscribe(console.log); // 50
Chanaka Weerasinghe
fuente
@VladKuts cambia los códigos y los atributos dados. Lamento las molestias.
Chanaka Weerasinghe
Gracias, ni siquiera me di cuenta de que podía almacenar operadores con capacidad de canalización como referencias de función y usarlos en la llamada de canalización (). Eso es mucho más limpio que hacerlo siempre en línea.
Alex. Un
9

Un buen resumen que se me ocurrió es:

Desacopla las operaciones de transmisión (mapear, filtrar, reducir ...) de la funcionalidad principal (suscripción, canalización). Al canalizar las operaciones en lugar de encadenar, no contamina el prototipo de Observable, lo que facilita la agitación de árboles.

Ver https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#why

Los problemas con los operadores parcheados para el encadenamiento de puntos son:

Cualquier biblioteca que importe un operador de parche aumentará el Observable.prototype para todos los consumidores de esa biblioteca, creando dependencias ciegas. Si la biblioteca elimina su uso, sin saberlo, rompen a todos los demás. Con pipeables, debe importar los operadores que necesita en cada archivo en el que los usa.

Los operadores parcheados directamente en el prototipo no son "modificables" con herramientas como rollup o webpack. Los operadores canalizables serán simplemente funciones extraídas directamente de los módulos.

Los operadores no utilizados que se están importando en aplicaciones no pueden ser detectados de manera confiable por ningún tipo de herramienta de construcción o regla de pelusa. Eso significa que puede importar el escaneo, pero dejar de usarlo y aún se agregará a su paquete de salida. Con los operadores de canalización, si no lo está utilizando, una regla de pelusa puede recogerlo por usted.

La composición funcional es asombrosa. La creación de sus propios operadores personalizados se vuelve mucho, mucho más fácil, y ahora funcionan y se ven como todos los demás operadores de rxjs. Ya no es necesario extender la elevación Observable o anular.

Juan Mendes
fuente
8

¿Cuál es la diferencia? Como ve en su ejemplo, la principal diferencia es mejorar la legibilidad del código fuente. Solo hay dos funciones en su ejemplo, pero imagínese si hay una docena de funciones. entonces será como

function1().function2().function3().function4()

se está poniendo realmente feo y difícil de leer, especialmente cuando estás llenando el interior de las funciones. Además de eso, ciertos editores como el código de Visual Studio no permiten más de 140 líneas de longitud. pero si va como seguir.

Observable.pipe(
function1(),
function2(),
function3(),
function4()
)

Esto mejora drásticamente la legibilidad.

Si no hay diferencia, ¿por qué existe la tubería de función? El propósito de la función PIPE () es agrupar todas las funciones que toman y devuelven observables. Toma un observable inicialmente, luego ese observable se usa en toda la función pipe () por cada función que se usa dentro de él.

La primera función toma el observable, lo procesa, modifica su valor y pasa a la siguiente función, luego la siguiente función toma la salida observable de la primera función, la procesa y pasa a la siguiente función, luego continúa hasta todas las funciones dentro de la función pipe () use ese observable, finalmente tiene el observable procesado. Al final, puede ejecutar la función observable con subscribe () para extraer el valor de ella. Recuerde, los valores del observable original no se modifican. !! 

¿Por qué esas funciones necesitan diferentes importaciones? Las importaciones dependen de dónde se especifique la función en el paquete rxjs. Dice así. Todos los módulos se almacenan en la carpeta node_modules en Angular. importar {clase} desde "módulo";

Tomemos el siguiente código como ejemplo. Lo acabo de escribir en stackblitz. Así que nada se genera automáticamente ni se copia desde otro lugar. No veo el punto de copiar lo que se indica en la documentación de rxjs cuando puede ir y leerlo también. Supongo que hizo esta pregunta aquí, porque no entendió la documentación. 

  • Hay clases de mapa de tubería, observables, de, importadas de los módulos respectivos. 
  • En el cuerpo de la clase, utilicé la función Pipe () como se ve en el código. 
  • La función Of () devuelve un observable, que emite números en secuencia cuando se suscribe.

  • Observable aún no está suscrito.

  • Cuando lo usó como Observable.pipe (), la función pipe () usa el Observable dado como entrada.

  • La primera función, la función map () usa ese Observable, lo procesa, devuelve el Observable procesado a la función pipe (),

  • entonces ese Observable procesado se da a la siguiente función si hay alguna,

  • y así continúa hasta que todas las funciones procesan el Observable,

  • al final que Observable es devuelto por la función pipe () a una variable, en el siguiente ejemplo su obs.

Ahora, lo que pasa en Observable es que, mientras el observador no lo suscriba, no emite ningún valor. Entonces utilicé la función subscribe () para suscribirme a este Observable, tan pronto como lo suscribí. La función of () comienza a emitir valores, luego se procesan a través de la función pipe () y se obtiene el resultado final al final, por ejemplo, se toma 1 de la función of (), 1 se agrega 1 en la función map (), y regresó. Puede obtener ese valor como argumento dentro de la función subscribe (función ( argumento ) {}).

Si desea imprimirlo, utilice como

subscribe( function (argument) {
    console.log(argument)
   } 
)
    import { Component, OnInit } from '@angular/core';
    import { pipe } from 'rxjs';
    import { Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit  {
    
      obs = of(1,2,3).pipe(
      map(x => x + 1),
      ); 
    
      constructor() { }
    
      ngOnInit(){  
        this.obs.subscribe(value => console.log(value))
      }
    }

https://stackblitz.com/edit/angular-ivy-plifkg

Don Dilanga
fuente