Wofür ist Pipe in RXJS?

95

Ich glaube, ich habe das Grundkonzept, aber es gibt einige Unklarheiten

Im Allgemeinen verwende ich also ein Observable wie folgt:

observable.subscribe(x => {

})

Wenn ich Daten filtern möchte, kann ich Folgendes verwenden:

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

})

Ich kann das auch machen:

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

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

})

Meine Fragen sind also:

  1. Was ist der Unterschied?
  2. Wenn es keinen Unterschied gibt, warum existiert die Funktionspipe?
  3. Warum benötigen diese Funktionen unterschiedliche Importe?
enno.void
quelle
1
Ich wollte gerade sagen, dass es für benutzerdefinierte, nicht native Operatoren ist, aber ich weiß nicht einmal, ob das richtig ist. Können pipe()Sie von Ihnen erstellte Operatoren übergeben?
Null298

Antworten:

66

Die Operatoren "pipable" (ehemals "lettable") sind die derzeitige und empfohlene Art der Verwendung von Operatoren seit RxJS 5.5.

Ich empfehle Ihnen dringend, die offizielle Dokumentation https://rxjs.dev/guide/v6/pipeable-operators zu lesen

Der Hauptunterschied besteht darin, dass es einfacher ist, benutzerdefinierte Operatoren zu erstellen, und dass es besser baumabrufbar ist, ohne dass einige globale Operatoren geändert werden Observable zu erstellen, Objekt zu , das Kollisionen verursachen könnte, wenn zwei verschiedene Parteien einen Operator mit demselben Namen erstellen .

Die Verwendung einer separaten importAnweisung für jeden Operator 'rxjs/add/operator/first'war eine Möglichkeit, kleinere App-Bundles zu erstellen. Indem Sie anstelle der gesamten RxJS-Bibliothek nur Operatoren importieren, die Sie benötigen, können Sie die Gesamtgröße des Bundles erheblich reduzieren. Der Compiler kann jedoch nicht wissen, ob Sie importiert haben, 'rxjs/add/operator/first'weil Sie es wirklich in Ihrem Code benötigen oder einfach vergessen haben, es beim Umgestalten Ihres Codes zu entfernen. Dies ist einer der Vorteile der Verwendung von Pipeline-Betreibern, bei denen nicht verwendete Importe automatisch ignoriert werden.

Martin
quelle
1
Zu Ihrer Bestätigung unused imports are ignored automaticallyverfügen die IDEs derzeit über Plugins, mit denen nicht verwendete Importe entfernt werden.
Silvanasono
Nicht jeder verwendet diese IDEs oder diese Plugins, viele Leute verwenden den einfachen Texteditor. Wahrscheinlich können wir die meiste Zeit nicht auf die Aussage zurückgreifen, dass jeder im Team denselben IDE- / Plugin-Set- / Texteditor verwendet wie wir.
Adam Faryna
3
@AdamFaryna sicher, einige Teams schreiben möglicherweise auch Code auf Papier, aber warum sollten sie, wenn sie über moderne Tools verfügen? Die Verwendung eines Texteditors, insbesondere ohne die wichtigen Plugins, ähnelt dem Schreiben von Code auf Papier. Sie können das tun, aber warum sollte ein anständiges Team / Entwickler das tun
Denes Papp
@DenesPapp Code Editor spielt keine Rolle, solange die Leute ihn produktiv nutzen können. Davon abgesehen sind es nur persönliche Vorlieben. Ihre Analogie zum Schreiben von Code auf Papier ist ungenau. Sie können keinen Code auf Papier ausführen, aber Code, der in einem beliebigen Texteditor geschrieben wurde, kann ausgeführt werden.
Adam Faryna
1
@perymimon Sie können aber Sie haben installieren rxjs-compatPaket github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/...
martin
11

Die Rohrmethode

Nach Originaldokumentation

Der pipable-Operator ist, dass die Funktion Observables als Eingabe verwendet und ein anderes Observable zurückgibt. Das vorherige Observable bleibt unverändert.

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

Ursprünglicher Beitrag

Was bedeutet Pfeife?

Das bedeutet, dass alle Operatoren, die Sie zuvor für die Instanz von Observable verwendet haben, als reine Funktionen unter verfügbar sind rxjs/operators. Dies macht das Erstellen einer Zusammensetzung von Operatoren oder das Wiederverwenden von Operatoren sehr einfach, ohne dass Sie auf alle Arten von Programmiergymnastik zurückgreifen müssen, bei denen Sie eine benutzerdefinierte beobachtbare Erweiterung erstellen müssen, die Observable erweitert, und dann den Lift überschreiben müssen, um Ihre eigene benutzerdefinierte Sache zu erstellen.

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
quelle
@VladKuts ändern Codes und gegebene Attribute .sorry für Unannehmlichkeiten.
Chanaka Weerasinghe
9

Eine gute Zusammenfassung, die ich mir ausgedacht habe, ist:

Es entkoppelt die Streaming-Operationen (Map, Filter, Reduce ...) von der Kernfunktionalität (Abonnieren, Piping). Durch Rohrleitungsvorgänge anstelle von Verkettungen wird der Prototyp von Observable nicht verschmutzt, was das Schütteln von Bäumen erleichtert.

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

Probleme mit den gepatchten Operatoren für die Punktverkettung sind:

Jede Bibliothek, die einen Patch-Operator importiert, erweitert den Observable.prototype für alle Benutzer dieser Bibliothek und erstellt blinde Abhängigkeiten. Wenn die Bibliothek ihre Nutzung aufhebt, brechen sie unwissentlich alle anderen. Bei Pipeables müssen Sie die benötigten Operatoren in jede Datei importieren, in der Sie sie verwenden.

Operatoren, die direkt auf den Prototyp gepatcht sind, können durch Tools wie Rollup oder Webpack nicht "baumschütteln". Pipeable Operatoren sind, da sie nur Funktionen sind, die direkt von Modulen abgerufen werden.

Nicht verwendete Operatoren, die in Apps importiert werden, können von keiner Art von Build-Tooling oder Flusenregel zuverlässig erkannt werden. Das bedeutet, dass Sie den Scan möglicherweise importieren, ihn jedoch nicht mehr verwenden und er weiterhin zu Ihrem Ausgabepaket hinzugefügt wird. Wenn Sie es bei Rohrleitungsbetreibern nicht verwenden, kann eine Flusenregel es für Sie abholen.

Die funktionale Komposition ist großartig. Das Erstellen eigener benutzerdefinierter Operatoren wird sehr viel einfacher, und jetzt funktionieren sie und sehen genauso aus wie alle anderen Operatoren von rxjs. Sie müssen Observable nicht mehr verlängern oder den Lift außer Kraft setzen.

Juan Mendes
quelle
6

Was ist der Unterschied? Wie Sie in Ihrem Beispiel sehen, besteht der Hauptunterschied darin, die Lesbarkeit des Quellcodes zu verbessern. In Ihrem Beispiel gibt es nur zwei Funktionen, aber stellen Sie sich vor, es gibt ein Dutzend Funktionen? dann wird es so gehen

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

Es wird wirklich hässlich und schwer zu lesen, besonders wenn Sie die Funktionen ausfüllen. Darüber hinaus erlauben bestimmte Editoren wie Visual Studio-Code nicht mehr als 140 Zeilen Länge. aber wenn es wie folgt geht.

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

Dies verbessert die Lesbarkeit drastisch.

Wenn es keinen Unterschied gibt, warum existiert die Funktionspipe? Der Zweck der PIPE () - Funktion besteht darin, sich zusammenzufassen alle Funktionen , die nehmen, und zurück zu beobachten. Zunächst wird ein Observable benötigt, dann wird dieses Observable von jeder darin verwendeten Funktion in der gesamten pipe () -Funktion verwendet.

Die erste Funktion nimmt das Observable, verarbeitet es, ändert seinen Wert und geht zur nächsten Funktion über. Die nächste Funktion nimmt das von der ersten Funktion beobachtbare Ergebnis, verarbeitet es und übergibt es zur nächsten Funktion. Dann geht es weiter, bis alle Funktionen erfüllt sind Verwenden Sie innerhalb der Pipe () -Funktion dieses Observable. Schließlich haben Sie das verarbeitete Observable. Am Ende können Sie die Funktion Observable with subscribe () ausführen, um den Wert daraus zu extrahieren. Denken Sie daran, dass die Werte in der ursprünglichen beobachtbaren Größe nicht geändert werden. !! 

Warum benötigen diese Funktionen unterschiedliche Importe? Importe hängen davon ab, wo die Funktion im Paket rxjs angegeben ist. Es geht so. Alle Module werden im Ordner node_modules in Angular gespeichert. {class} aus "module" importieren;

Nehmen wir als Beispiel den folgenden Code. Ich habe es gerade in Stackblitz geschrieben. Es wird also nichts automatisch generiert oder von einem anderen Ort kopiert. Ich sehe keinen Sinn darin, das zu kopieren, was in der Dokumentation von rxjs angegeben ist, wenn Sie es auch lesen können. Ich gehe davon aus, dass Sie diese Frage hier gestellt haben, weil Sie die Dokumentation nicht verstanden haben. 

  • Es gibt beobachtbare Pipe-Map-Klassen, die aus den jeweiligen Modulen importiert wurden. 
  • Im Hauptteil der Klasse habe ich die Pipe () -Funktion verwendet, wie im Code gezeigt. 
  • Die Of () -Funktion gibt ein Observable zurück, das beim Abonnieren nacheinander Zahlen ausgibt.

  • Observable ist noch nicht abonniert.

  • Wenn Sie Observable.pipe () mögen, verwendet die Funktion pipe () das angegebene Observable als Eingabe.

  • Die erste Funktion, map (), verwendet diese Observable, verarbeitet sie und gibt die verarbeitete Observable an die pipe () -Funktion zurück.

  • dann wird das verarbeitete Observable an die nächste Funktion übergeben, falls es welche gibt,

  • und es geht so weiter, bis alle Funktionen das Observable verarbeiten,

  • Am Ende wird Observable von der Funktion pipe () an eine Variable zurückgegeben. Im folgenden Beispiel ist es obs.

Nun ist die Sache in Observable: Solange der Beobachter es nicht abonniert hat, gibt es keinen Wert aus. Also habe ich die Funktion subscribe () verwendet, um dieses Observable zu abonnieren, und sobald ich es abonniert habe. Die Funktion of () gibt Werte aus, dann werden sie über die Funktion pipe () verarbeitet, und am Ende erhalten Sie das Endergebnis. Beispiel: 1 wird aus der Funktion () übernommen, 1 wird 1 in der Funktion map () hinzugefügt. und kehrte zurück. Sie können diesen Wert als Argument innerhalb der Funktion subscribe (function ( argument ) {}) abrufen.

Wenn Sie es drucken möchten, verwenden Sie als

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
quelle