A cosa serve pipe in rxJS


104

Penso di avere il concetto di base, ma ci sono alcune oscurità

Quindi in generale questo è il modo in cui utilizzo un osservabile:

observable.subscribe(x => {

})

Se voglio filtrare i dati posso usare questo:

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

})

Posso fare anche questo:

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

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

})

Quindi le mie domande sono:

  1. Qual è la differenza?
  2. Se non c'è differenza, perché esiste la funzione pipe?
  3. Perché queste funzioni richiedono importazioni diverse?

1
Stavo per dire che è per operatori personalizzati, non nativi, ma non so nemmeno se sia giusto. Ti pipe()consente di passare gli operatori che crei?
zero298

Risposte:


69

Gli operatori "pipable" (ex "lettable") sono il modo corrente e consigliato di utilizzare gli operatori a partire da RxJS 5.5.

Ti consiglio vivamente di leggere la documentazione ufficiale https://rxjs.dev/guide/v6/pipeable-operators

La differenza principale è che è più facile creare operatori personalizzati e che è meglio eseguire il treehakable senza alterare alcun Observableoggetto globale che potrebbe creare collisioni se due parti diverse volessero creare un operatore con lo stesso nome.

L'utilizzo di importistruzioni separate per ogni operatore 'rxjs/add/operator/first'era un modo per creare bundle di app più piccoli. Importando solo gli operatori necessari al posto dell'intera libreria RxJS è possibile ridurre in modo significativo la dimensione totale del bundle. Tuttavia il compilatore non può sapere se hai importato 'rxjs/add/operator/first'perché ne hai davvero bisogno nel codice o ti sei semplicemente dimenticato di rimuoverlo durante il refactoring del codice. Questo è uno dei vantaggi dell'utilizzo di operatori pipable in cui le importazioni inutilizzate vengono ignorate automaticamente.


1
Riguardo alla tua affermazione unused imports are ignored automatically, attualmente gli IDE hanno plugin che rimuovono le importazioni inutilizzate.
silvanasono

Non tutti usano questi IDE o questi plugin, molte persone usano un editor di testo di base. Probabilmente la maggior parte delle volte non possiamo basarci sull'affermazione che tutti i membri del team utilizzano lo stesso IDE / set di plug-in / editor di testo come noi.
Adam Faryna,

3
@AdamFaryna certo, alcuni team possono anche scrivere codice su carta, ma perché dovrebbero se hanno a disposizione strumenti moderni? Usare un editor di testo, soprattutto senza i plugin importanti, è simile alla scrittura di codice su carta. Puoi farlo, ma perché un team / sviluppatore decente dovrebbe farlo
Denes Papp

L'editor di codice @DenesPapp non ha importanza finché le persone possono usarlo in modo produttivo. Oltre a questo, sono solo preferenze personali. La tua analogia con la scrittura di codice su carta è imprecisa, non puoi eseguire codice su carta ma il codice scritto in qualsiasi editor di testo può essere eseguito.
Adam Faryna

1
@perymimon Puoi ma devi installare il rxjs-compatpacchetto github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
martin

16

Il metodo pipe

Secondo la documentazione originale

l'operatore pipable è che la funzione accetta osservabili come input e restituisce un altro osservabile. L'osservabile precedente rimane invariato.

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

Post originale

Cosa significa pipa?

Ciò significa che tutti gli operatori precedentemente utilizzati sull'istanza di osservabile sono disponibili come funzioni pure sotto rxjs/operators. Questo rende la costruzione di una composizione di operatori o il riutilizzo di operatori diventa davvero facile, senza dover ricorrere a tutti i tipi di ginnastica di programmazione in cui devi creare un osservabile personalizzato estendibile Observable, quindi sovrascrivere l'ascensore solo per creare la tua cosa personalizzata.

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

@VladKuts cambia i codici e gli attributi forniti. Ci scusiamo per l'inconveniente.
Chanaka Weerasinghe

Grazie, non mi ero nemmeno reso conto di poter memorizzare operatori pipe-grado come riferimenti a funzioni e usarli nella chiamata pipe (). È molto più pulito che farlo sempre in linea.
Alex. Un

9

Un buon riassunto che ho trovato è:

Disaccoppia le operazioni di streaming (mappa, filtro, riduzione ...) dalla funzionalità principale (sottoscrizione, piping). Effettuando operazioni di piping invece di concatenare, non inquina il prototipo di Observable, rendendo più facile lo scuotimento degli alberi.

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

I problemi con gli operatori patchati per il dot-chaining sono:

Qualsiasi libreria che importi un operatore di patch aumenterà il prototipo Observable.per tutti i consumatori di quella libreria, creando dipendenze cieche. Se la libreria rimuove il loro utilizzo, inconsapevolmente rompono tutti gli altri. Con i pipeable, devi importare gli operatori di cui hai bisogno in ogni file in cui li usi.

Gli operatori patchati direttamente sul prototipo non sono "modificabili dagli alberi" da strumenti come il rollup o il webpack. Gli operatori pipeable saranno poiché sono solo funzioni estratte direttamente dai moduli.

Gli operatori inutilizzati che vengono importati nelle app non possono essere rilevati in modo affidabile da alcun tipo di strumenti di creazione o regola di lanugine. Ciò significa che potresti importare la scansione, ma smettere di usarla e verrà ancora aggiunta al tuo pacchetto di output. Con gli operatori pipeable, se non lo stai usando, una regola di lanugine può prenderlo per te.

La composizione funzionale è fantastica. Costruire i tuoi operatori personalizzati diventa molto, molto più semplice e ora funzionano e hanno lo stesso aspetto di tutti gli altri operatori di rxjs. Non è più necessario estendere l'osservabile o ignorare il sollevamento.


8

Qual è la differenza? Come vedi nel tuo esempio, la differenza principale è migliorare la leggibilità del codice sorgente. Ci sono solo due funzioni nel tuo esempio, ma immagina se ci sono una dozzina di funzioni? poi andrà come

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

sta diventando davvero brutto e difficile da leggere, specialmente quando si riempiono le funzioni. Inoltre, alcuni editor come il codice di Visual Studio non consentono più di 140 righe di lunghezza. ma se va come segue.

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

Questo migliora drasticamente la leggibilità.

Se non c'è differenza, perché esiste la funzione pipe? Lo scopo della funzione PIPE () è raggruppare tutte le funzioni che accettano e restituiscono osservabili. Inizialmente richiede un osservabile, quindi quell'osservabile viene utilizzato in tutta la funzione pipe () da ciascuna funzione utilizzata al suo interno.

La prima funzione prende l'osservabile, lo elabora, modifica il suo valore e passa alla funzione successiva, quindi la funzione successiva prende l'output osservabile della prima funzione, lo elabora e passa alla funzione successiva, quindi va avanti fino a tutte le funzioni all'interno della funzione pipe () usa quell'osservabile, finalmente hai l'osservabile elaborato. Alla fine è possibile eseguire l'osservabile con la funzione subscribe () per estrarne il valore. Ricorda, i valori nell'originale osservabile non vengono modificati. !! 

Perché queste funzioni richiedono importazioni diverse? Le importazioni dipendono da dove è specificata la funzione nel pacchetto rxjs. Va così. Tutti i moduli sono archiviati nella cartella node_modules in Angular. importa {class} da "modulo";

Prendiamo il codice seguente come esempio. L'ho appena scritto in Stackblitz. Quindi nulla viene generato automaticamente o copiato da qualche altra parte. Non vedo il punto di copiare quanto dichiarato nella documentazione di rxjs quando puoi andare a leggerlo anche tu. Presumo che tu abbia posto questa domanda qui, perché non hai capito la documentazione. 

  • Ci sono pipe, osservabili, di classi map importate dai rispettivi moduli. 
  • Nel corpo della classe, ho usato la funzione Pipe () come mostrato nel codice. 
  • La funzione Of () restituisce un osservabile, che emette numeri in sequenza quando viene sottoscritto.

  • Observable non è ancora iscritto.

  • Quando l'hai usato piace Observable.pipe (), la funzione pipe () usa il dato Observable come input.

  • La prima funzione, map (), usa quell'Osservabile, lo elabora, restituisce l'Osservabile elaborato alla funzione pipe (),

  • quindi quell'Osservabile elaborato viene assegnato alla funzione successiva, se presente,

  • e va avanti così finché tutte le funzioni elaborano l'osservabile,

  • alla fine che Observable viene restituito dalla funzione pipe () a una variabile, nell'esempio seguente è obs.

Ora la cosa in Observable è, finché l'osservatore non l'ha sottoscritto, non emette alcun valore. Quindi ho usato la funzione subscribe () per iscrivermi a questo Observable, quindi non appena l'ho sottoscritto. La funzione of () inizia a emettere valori, quindi vengono elaborati tramite la funzione pipe () e alla fine si ottiene il risultato finale, ad esempio 1 viene preso dalla funzione of (), 1 viene aggiunto 1 nella funzione map (), e tornò indietro. Puoi ottenere quel valore come argomento all'interno della funzione subscribe (funzione ( argomento ) {}).

Se vuoi stamparlo, usa come

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.