Do czego służy rura w RxJS?

110

Myślę, że mam podstawową koncepcję, ale są pewne niejasności

Tak więc ogólnie używam Observable:

observable.subscribe(x => {

})

Jeśli chcę filtrować dane, mogę użyć tego:

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

})

Mogę też to zrobić:

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

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

})

Więc moje pytania to:

  1. Jaka jest różnica?
  2. Jeśli nie ma różnicy, dlaczego ta funkcja pipeistnieje?
  3. Dlaczego te funkcje wymagają różnych importów?
enno.void
źródło
1
Już miałem powiedzieć, że to jest dla niestandardowych, nienatywnych operatorów, ale nawet nie wiem, czy to prawda. Czy pipe()pozwala mijać operatorów, których tworzysz?
zero298

Odpowiedzi:

73

Operatory „pipable” (dawniej „lettable”) to obecny i zalecany sposób używania operatorów od wersji RxJS 5.5.

Gorąco polecam przeczytanie oficjalnej dokumentacji https://rxjs.dev/guide/v6/pipeable-operators

Główną różnicą jest to, że łatwiej jest tworzyć niestandardowe operatory i że jest to łatwiejsze do zmiany w drzewie, bez zmiany jakiegoś globalnego Observableobiektu, który mógłby spowodować kolizje, gdyby dwie różne strony chciały utworzyć operatora o tej samej nazwie.

Użycie oddzielnych importinstrukcji dla każdego operatora 'rxjs/add/operator/first'było sposobem na tworzenie mniejszych pakietów aplikacji. Importując tylko potrzebne operatory zamiast całej biblioteki RxJS, możesz znacznie zmniejszyć całkowity rozmiar pakietu. Jednak kompilator nie może wiedzieć, czy zaimportowałeś go, 'rxjs/add/operator/first'ponieważ naprawdę potrzebujesz go w swoim kodzie, czy po prostu zapomniałeś go usunąć podczas refaktoryzacji kodu. To jedna z zalet korzystania z operatorów rurociągów, w których niewykorzystane importy są automatycznie ignorowane.

jaskółka oknówka
źródło
1
O twojej afirmacji unused imports are ignored automatically, obecnie IDE mają wtyczki, które usuwają nieużywane importy.
silvanasono
Nie wszyscy używają tych IDE lub tych wtyczek, wiele osób używa podstawowego edytora tekstu. Prawdopodobnie przez większość czasu nie możemy polegać na stwierdzeniu, że wszyscy w zespole używają tego samego IDE / zestawu wtyczek / edytora tekstu co my.
Adam Faryna
3
@AdamFaryna Jasne, niektóre zespoły mogą również pisać kod na papierze, ale dlaczego mieliby to robić, skoro mają dostępne nowoczesne narzędzia? Korzystanie z edytora tekstu, zwłaszcza bez ważnych wtyczek, jest podobne do pisania kodu na papierze. Możesz to zrobić, ale dlaczego jakikolwiek przyzwoity zespół / programista miałby to robić
Denes Papp
Edytor kodu @DenesPapp nie ma znaczenia, ponieważ ludzie mogą go używać w produktywny sposób. Poza tym to tylko osobiste preferencje. Twoja analogia do pisania kodu na papierze jest niedokładna, nie możesz wykonać kodu na papierze, ale kod napisany w dowolnym edytorze tekstu może zostać wykonany.
Adam Faryna
1
@perymimon Możesz, ale musisz zainstalować rxjs-compatpakiet github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/ ...
martin
16

Metoda rurowa

Zgodnie z oryginalną dokumentacją

operator pipable polega na tym, że funkcja przyjmuje obserowalne jako dane wejściowe i zwraca inną obserwowalną.

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

Oryginalny post

Co oznacza rura?

Oznacza to, że wszystkie operatory, których poprzednio używałeś na instancji obserwo walnego, są dostępne jako czyste funkcje w rxjs/operators. To sprawia, że ​​tworzenie kompozycji operatorów lub ponowne użycie operatorów staje się naprawdę łatwe, bez konieczności uciekania się do wszelkiego rodzaju gimnastyki programistycznej, w której musisz utworzyć niestandardową obserwowalną rozszerzającą się Observable, a następnie nadpisać windę, aby stworzyć własną niestandardową rzecz.

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
źródło
@VladKuts zmienia kody i podane atrybuty. Przepraszamy za niedogodności.
Chanaka Weerasinghe
Dziękuję, nawet nie zdawałem sobie sprawy, że mogę przechowywać operatory potokowe jako odwołania do funkcji i używać ich w wywołaniu pipe (). To znacznie czystsze niż zawsze, gdy robisz to w linii.
Alex.
10

Dobre podsumowanie, które wymyśliłem, to:

Oddziela operacje przesyłania strumieniowego (mapowanie, filtrowanie, redukowanie ...) od podstawowych funkcji (subskrypcja, potoki). Poprzez operacje na rurociągach zamiast tworzenia łańcuchów, nie zanieczyszcza prototypu Observable, ułatwiając potrząsanie drzewami.

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

Problemy z załatanymi operatorami do łączenia kropek to:

Każda biblioteka, która importuje operator łatki, rozszerzy prototyp Observable. dla wszystkich konsumentów tej biblioteki, tworząc ślepe zależności. Jeśli biblioteka usunie ich użycie, nieświadomie niszczą wszystkich innych. W przypadku Pipeables musisz zaimportować potrzebne operatory do każdego pliku, w którym ich używasz.

Operatorów załatanych bezpośrednio na prototypie nie można „wstrząsnąć drzewem” za pomocą narzędzi takich jak rollup lub webpack. Operatorzy rurowi będą tacy, jak funkcje pobierane bezpośrednio z modułów.

Nieużywane operatory, które są importowane w aplikacjach, nie mogą być niezawodnie wykrywane przez jakiekolwiek narzędzia do kompilacji lub reguły lint. Oznacza to, że możesz zaimportować skan, ale przestań go używać i nadal jest dodawany do pakietu wyjściowego. W przypadku operatorów rurociągów, jeśli ich nie używasz, reguła kłaczków może je odebrać.

Funkcjonalna kompozycja jest niesamowita. Tworzenie własnych operatorów niestandardowych staje się dużo, dużo łatwiejsze, a teraz działają i wyglądają tak samo jak wszystkie inne operatory z rxjs. Nie musisz już przedłużać Observable ani override lift.

Juan Mendes
źródło
9

Jaka jest różnica? Jak widać w swoim przykładzie, główna różnica polega na poprawie czytelności kodu źródłowego. W twoim przykładzie są tylko dwie funkcje, ale wyobraź sobie, że jest ich kilkanaście? wtedy pójdzie jak

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

robi się naprawdę brzydki i trudny do odczytania, zwłaszcza podczas wypełniania funkcji. Ponadto niektóre edytory, takie jak kod programu Visual Studio, nie zezwalają na więcej niż 140 linii. ale jeśli pójdzie jak podążanie.

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

To drastycznie poprawia czytelność.

Jeśli nie ma różnicy, dlaczego istnieje potok funkcyjny? Celem funkcji PIPE () jest zsumowanie wszystkich funkcji, które przyjmują i zwraca obserwowalne. Najpierw pobiera obserowalną, a następnie ta obserwowalna jest używana w całej funkcji pipe () przez każdą funkcję używaną wewnątrz niej.

Pierwsza funkcja przyjmuje obserwowalną funkcję, przetwarza ją, modyfikuje jej wartość i przechodzi do następnej funkcji, a następnie następna funkcja pobiera obserwowalne dane wyjściowe pierwszej funkcji, przetwarza je i przekazuje do następnej funkcji, a następnie kontynuuje działanie aż do wszystkich funkcji wewnątrz funkcji pipe () użyj tego obserwa- walnego, w końcu masz przetworzony obserowalny. Na koniec możesz wykonać obserwowalną funkcję subscribe (), aby wydobyć z niej wartość. Pamiętaj, że wartości w oryginalnej obserwowalnej nie ulegają zmianie. !! 

Dlaczego te funkcje wymagają różnych importów? Importy zależą od tego, gdzie funkcja jest określona w pakiecie rxjs. Tak to wygląda. Wszystkie moduły są przechowywane w folderze node_modules w Angular. import {class} z "modułu";

Weźmy następujący kod jako przykład. Właśnie napisałem to w stackblitz. Nic więc nie jest generowane automatycznie ani kopiowane z innego miejsca. Nie widzę sensu kopiowania tego, co jest napisane w dokumentacji rxjs, kiedy możesz to przeczytać i przeczytać. Zakładam, że zadałeś to pytanie, ponieważ nie zrozumiałeś dokumentacji. 

  • Istnieją klasy potoku, które można zaobserwować, importowane z odpowiednich modułów. 
  • W treści klasy użyłem funkcji Pipe (), jak widać w kodzie. 
  • Funkcja Of () zwraca obserwowalny, który emituje liczby w kolejności, gdy jest subskrybowany.

  • Observable nie jest jeszcze subskrybowany.

  • Kiedy użyłeś tego, lubi Observable.pipe (), funkcja pipe () używa danego Observable jako wejścia.

  • Pierwsza funkcja, map () używa tego Observable, przetwarza go, zwraca przetworzony Observable z powrotem do funkcji pipe (),

  • wtedy ta przetworzona Observable jest przekazywana do następnej funkcji, jeśli istnieje,

  • i tak się dzieje, dopóki wszystkie funkcje nie przetworzą Observable,

  • na końcu funkcja pipe () zwraca wartość Observable do zmiennej, w poniższym przykładzie jej obs.

Rzecz w Observable polega na tym, że dopóki obserwator go nie subskrybuje, nie emituje żadnej wartości. Więc użyłem funkcji subscribe (), aby zasubskrybować to Observable, a potem jak tylko je zasubskrybowałem. Funkcja of () zaczyna emitować wartości, następnie są one przetwarzane przez funkcję pipe (), a na końcu otrzymujesz wynik końcowy, na przykład 1 jest pobierane z funkcji of (), 1 jest dodawane 1 w funkcji map (), i wrócił. Możesz pobrać tę wartość jako argument wewnątrz funkcji subscribe (funkcja ( argument ) {}).

Jeśli chcesz go wydrukować, użyj jako

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
źródło