Warum müssen wir flatMap verwenden?


91

Ich fange an, RxJS zu verwenden, und ich verstehe nicht, warum wir in diesem Beispiel eine Funktion wie flatMapoder verwenden müssen concatAll. Wo ist das Array von Arrays hier?

var requestStream = Rx.Observable.just('https://api.github.com/users');

var responseMetastream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

responseMetastream.subscribe(url => {console.log(url)})

Wenn jemand visuell erklären kann, was passiert, ist dies sehr hilfreich.


1
Diese Antwort ist großartig, weil sie wertvolle Referenzen enthält, aber die Terminologie von rxjs lässt sich nicht gut ins Englische übersetzen. (Bilder sind besser). Aus diesem Grund empfehle ich, stattdessen einfache Beispiele wie dieses oder komplexere Beispiele im rxjs-Repo auszuführen und ".do" -Operatoren vor und nach einem Flatmap- und Map-Operator hinzuzufügen und dann mit dem Chrome-Debugger einen Haltepunkt festzulegen. Sie werden sofort sehen, dass jeder eine andere Ausgabe erzeugt
HipsterZipster

4
Ich denke, wenn flatMapes benannt worden wäre mapThenFlatten, wäre es weniger verwirrend.
Ziege

Antworten:


72

Als ich anfing, einen Blick darauf zu werfen, Rxjsstolperte ich auch über diesen Stein. Was mir geholfen hat, ist Folgendes:

  • Dokumentation von reactivex.io. Zum Beispiel für flatMap: http://reactivex.io/documentation/operators/flatmap.html
  • Dokumentation von rxmarbles: http://rxmarbles.com/ . Sie werden dort nicht finden flatMap, Sie müssen mergeMapstattdessen schauen (ein anderer Name).
  • die Einführung zu Rx, die Sie vermisst haben: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Es wird ein sehr ähnliches Beispiel angesprochen. Insbesondere wird die Tatsache angesprochen, dass ein Versprechen einem beobachtbaren Wert ähnelt, der nur einen Wert ausstrahlt.
  • Schauen Sie sich endlich die Typinformationen von RxJava an. Nicht getipptes Javascript hilft hier nicht. Grundsätzlich Observable<T>bezeichnet if ein beobachtbares Objekt, das Werte vom Typ T flatMapüberträgt, eine Funktion vom Typ T' -> Observable<T>als Argument und gibt sie zurück Observable<T>.mapübernimmt eine Funktion vom Typ T' -> Tund gibt zurück Observable<T>.

    Zurück zu Ihrem Beispiel: Sie haben eine Funktion, die Versprechen aus einer URL-Zeichenfolge erzeugt. Also T' : stringund T : promise. Und von dem, was wir vorher gesagt haben promise : Observable<T''>, also T : Observable<T''>mit T'' : html. Wenn Sie diese vielversprechende Funktion einsetzen map, erhalten Sie, Observable<Observable<T''>>wann Sie wollen Observable<T''>: Sie möchten, dass das Observable die htmlWerte ausgibt. flatMapwird so genannt, weil es das Ergebnis von abflacht (eine beobachtbare Schicht entfernt) map. Abhängig von Ihrem Hintergrund mag dies für Sie chinesisch sein, aber mit den Tippinformationen und der Zeichnung von hier wurde mir alles klar: http://reactivex.io/documentation/operators/flatmap.html .


2
Ich habe vergessen zu erwähnen, dass Sie in der Lage sein sollten, zu vereinfachen return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));, return jQuery.getJSON(requestUrl);da flatMapauch eine Auswahlfunktion akzeptiert wird, die ein Versprechen zurückgibt, dh eine Funktion vom Typ T' -> Promise.
user3743222

2
Wow, dieser GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) ist verdammt fantastisch. Ich empfehle es jedem, der mit ReactiveX-Bibliotheken wie RxJS arbeitet.
Jacob Stamm

@ JacobStamm Ich stimme zu. Macht es einfach einfacher.
CruelEngine

Was bedeutet diese Syntax : T’ -> T? Ich verstehe das Tals Generikum, aber was ist der Apostroph und der fettfreie Pfeil?
1252748

Sie können T 'durch X oder Y ersetzen, ohne die Bedeutung irgendwo in der Antwort zu ändern. Der Pfeil ist die Haskell-Notation für die Typensignatur. T '-> T ist also die Signatur für eine Funktion, die ein Element vom Typ T' nimmt und ein Element vom Typ T
zurückgibt

123
['a','b','c'].flatMap(function(e) {
    return [e, e+ 'x', e+ 'y',  e+ 'z'  ];
});
//['a', 'ax', 'ay', 'az', 'b', 'bx', 'by', 'bz', 'c', 'cx', 'cy', 'cz']


['a','b','c'].map(function(e) {
    return [e, e+ 'x', e+ 'y',  e+ 'z'  ];
});
//[Array[4], Array[4], Array[4]]

Sie verwenden flatMap, wenn Sie ein Observable haben, dessen Ergebnisse mehr Observables sind.

Wenn Sie ein Observable haben, das von einem anderen Observable erzeugt wird, können Sie es nicht direkt filtern, reduzieren oder abbilden, da Sie ein Observable haben, das nicht die Daten enthält. Wenn Sie eine beobachtbare Datei erstellen, wählen Sie flatMap anstelle der Karte. dann bist du okay

Wie im zweiten Snippet müssen Sie flatMap verwenden, wenn Sie einen asynchronen Vorgang ausführen.

var source = Rx.Observable.interval(100).take(10).map(function(num){
    return num+1
});
source.subscribe(function(e){
    console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>

var source = Rx.Observable.interval(100).take(10).flatMap(function(num){
    return Rx.Observable.timer(100).map(() => num)
});
source.subscribe(function(e){
    console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>


31

flatMap Verwandeln Sie die von einem Observable emittierten Elemente in neue Observables und glätten Sie dann die Emissionen aus diesen in ein einzelnes Observable.

Schauen Sie sich das folgende Szenario an, get("posts")in dem ein Observable zurückgegeben wird, das von "abgeflacht" wird flatMap.

myObservable.map(e => get("posts")).subscribe(o => console.log(o));
// this would log Observable objects to console.  

myObservable.flatMap(e => get("posts")).subscribe(o => console.log(o));
// this would log posts to console.

2
Schöne, einfache Antwort. Ich denke, das könnte das Beste sein.
Vaughan

"flatMap wandelt die von einem Observable emittierten Elemente in neue Observables um und reduziert dann die Emissionen von diesen in ein einzelnes Observable." Das ist ausgezeichnetes Zeug.
MBak

30

Menschen neigen dazu, Dinge zu komplizieren, indem sie die Definition geben, die besagt:

flatMap wandelt die von einem Observable emittierten Elemente in Observables um und reduziert dann die Emissionen von diesen in ein einzelnes Observable

Ich schwöre, diese Definition verwirrt mich immer noch, aber ich werde sie auf einfachste Weise anhand eines Beispiels erklären

Unsere Situation : Wir haben ein Observable, das Daten zurückgibt (einfache URL), die wir verwenden werden, um einen HTTP-Aufruf durchzuführen, der ein Observable zurückgibt, das die benötigten Daten enthält, damit Sie die Situation wie folgt visualisieren können:

Observable 1
    |_
       Make Http Call Using Observable 1 Data (returns Observable_2)
            |_
               The Data We Need

Wie Sie sehen, können wir die benötigten Daten nicht direkt erreichen. Als erste Möglichkeit, die Daten abzurufen, können wir nur normale Abonnements wie diese verwenden:

Observable_1.subscribe((URL) => {
         Http.get(URL).subscribe((Data_We_Need) => {
                  console.log(Data_We_Need);
          });
});

Dies funktioniert, aber wie Sie sehen, müssen wir Abonnements verschachteln, um unsere Daten zu erhalten. Dies sieht derzeit nicht schlecht aus. Stellen Sie sich jedoch vor, wir haben 10 verschachtelte Abonnements, die nicht mehr gewartet werden können.

Ein besserer Weg, dies zu handhaben, besteht darin, nur den Operator zu verwenden, der flatMapdas Gleiche tut, aber dieses verschachtelte Abonnement vermeidet:

Observable_1
    .flatMap(URL => Http.get(URL))
    .subscribe(Data_We_Need => console.log(Data_We_Need));

18

Einfach:

[1,2,3].map(x => [x, x * 10])
// [[1, 10], [2, 20], [3, 30]]

[1,2,3].flatMap(x => [x, x * 10])
// [1, 10, 2, 20, 3, 30]]

16

Es ist kein Array von Arrays. Es ist ein Observable von Observable (s).

Im Folgenden wird ein beobachtbarer Zeichenfolgenstrom zurückgegeben.

requestStream
  .map(function(requestUrl) {
    return requestUrl;
  });

Während dies einen beobachtbaren Strom von beobachtbarem Strom von json zurückgibt

requestStream
  .map(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

flatMap glättet das Observable automatisch für uns, damit wir den JSON-Stream direkt beobachten können


3
Es ist schwer, dieses Konzept zu verstehen. Können Sie bitte Kommentare hinzufügen, um zu visualisieren, was Sie meinen "gibt einen beobachtbaren Strom von beobachtbaren Strömen von JSON zurück". Vielen Dank.
user233232

@ user233232, wie [x, x, x, x] bis [[xxx], [[xxx], [xxx]]
serkan

Der Schlüssel zum Verständnis des ersten Satzes ist das Verständnis, dass flatMap(und map) für Arrays nichts Besonderes sind. Es ist möglich, diese Operationen für jeden generischen Container oder Wrapper zu definieren, einschließlich Arrays, Wörterbücher, "Optionals", reaktive Streams, Versprechen, Zeiger und sogar Funktionen selbst. Dies ist eine emergente Eigenschaft eines mathematischen Konstrukts namens Monade. Alle obigen Beispiele erfüllen die Anforderungen, um eine Monade zu sein, und daher können sie alle eine Definition von mapund a erhalten flatMap(mit einigen Einschränkungen).
mklbtz

13

Ein Observable ist ein Objekt, das einen Strom von Ereignissen ausgibt: Next, Error und Completed.

Wenn Ihre Funktion eine Observable zurückgibt, gibt sie keinen Stream zurück, sondern eine Instanz von Observable. Der flatMapBediener ordnet diese Instanz einfach einem Stream zu.

Dies ist das Verhalten im flatMapVergleich zu map: Führen Sie die angegebene Funktion aus und reduzieren Sie das resultierende Objekt zu einem Stream.


13

Hier wird die äquivalente Implementierung einer flatMap mithilfe von Abonnements gezeigt.

Ohne flatMap:

this.searchField.valueChanges.debounceTime(400)
.subscribe(
  term => this.searchService.search(term)
  .subscribe( results => {
      console.log(results);  
      this.result = results;
    }
  );
);

Mit flatMap:

this.searchField.valueChanges.debounceTime(400)
    .flatMap(term => this.searchService.search(term))
    .subscribe(results => {
      console.log(results);
      this.result = results;
    });

http://plnkr.co/edit/BHGmEcdS5eQGX703eRRE?p=preview

Hoffe es könnte helfen.

Olivier.


7

Mit flatMap

var requestStream = Rx.Observable.just('https://api.github.com/users');

var responseMetastream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

responseMetastream.subscribe(json => {console.log(json)})

Ohne flatMap

var requestStream = Rx.Observable.just('https://api.github.com/users');

var responseMetastream = requestStream
  .map(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

responseMetastream.subscribe(jsonStream => {
  jsonStream.subscribe(json => {console.log(json)})
})
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.