it-swarm.com.de

Wie funktioniert RxJS MergeMap?

Ich verstehe den Zweck von mergeMap überhaupt nicht. Ich habe zwei Erklärungen gehört:

  1. "Es ist wie SelectAll" in LINQ - nein.
  2. "Nun, es ist eine Kombination aus RxJS merge und map" - nein (oder ich kann das nicht replizieren).

Betrachten Sie den folgenden Code:

    var obs1 = new Rx.Observable.interval(1000);
    var obs2 = new Rx.Observable.interval(1000);

    //Just a merge and a map, works fine
    obs1.merge(obs2).map(x=> x+'a').subscribe(
      next => console.log(next)
    )

    //Who know what - seems to do the same thing as a plain map on 1 observable
    obs1.mergeMap(val => Rx.Observable.of(val + `B`))
        .subscribe(
          next => console.log(next)
        )

JS Bin

Das letzte Stück mit der Aufschrift "Wer weiß was" ist nichts anderes als eine Karte auf obs1 - Was ist der Punkt?

Was macht mergeMap eigentlich? Was ist ein Beispiel für einen gültigen Anwendungsfall? (Am liebsten mit etwas Code)

Artikel, die mir überhaupt nicht geholfen haben (MergeMap-Code von oben stammt von einem dieser): 1 , 2

44
VSO

tl; dr;mergeMap ist viel mächtiger als map. Das Verstehen von mergeMap ist die notwendige Bedingung, um auf die volle Leistung von Rx zugreifen zu können.


ähnlichkeiten

  • sowohl mergeMap als auch map wirken auf einen einzelnen Stream (im Vergleich zu Zip, combineLatest)

  • sowohl mergeMap als auch map können Elemente eines Streams transformieren (im Vergleich zu filter, delay)

unterschiede

karte

  • kann die Größe des Quelldatenstroms nicht ändern (Annahme: map selbst nicht throw); für jedes Element aus der Quelle wird genau ein mapped Element ausgegeben; map kann keine Elemente ignorieren (wie zum Beispiel filter);

  • im Falle des Standard-Schedulers erfolgt die Umwandlung synchron; um 100% klar zu sein: der Quell-Stream kann seine Elemente asynchron liefern, aber jedes nächste Element wird sofort mapped und erneut ausgegeben; map kann Elemente nicht zeitlich verschieben, wie zum Beispiel delay

  • keine Einschränkung der Rückgabewerte

  • id: x => x

mergeMap

  • kann die Größe des Quellstroms ändern; Für jedes Element kann eine beliebige Anzahl (0, 1 oder viele) neuer Elemente erstellt/ausgegeben werden

  • es bietet die volle Kontrolle über die Asynchronität - sowohl wenn neue Elemente erstellt/ausgegeben werden als auch wie viele Elemente aus dem Quelldatenstrom gleichzeitig verarbeitet werden sollen. Nehmen wir beispielsweise an, der Quelldatenstrom hat 10 Elemente ausgegeben, aber maxConcurrency ist auf 2 gesetzt. Dann werden sofort zwei erste Elemente verarbeitet und die restlichen 8 gepuffert. Sobald eines der verarbeiteten Elemente completed verarbeitet wird, wird das nächste Element aus dem Quelldatenstrom verarbeitet

  • alle anderen Operatoren können mit den Konstruktoren mergeMap und Observable implementiert werden

  • kann für rekursive asynchrone Operationen verwendet werden

  • rückgabewerte müssen vom Typ Observable sein (oder Rx muss wissen, wie man Observable daraus erstellt - z. B. Versprechen, Array)

  • id: x => Rx.Observable.of(x)

array-Analogie

let array = [1,2,3]
fn             map                    mergeMap
x => x*x       [1,4,9]                error /*expects array as return value*/
x => [x,x*x]   [[1,1],[2,4],[3,9]]    [1,1,2,4,3,9]

Die Analogie zeigt kein vollständiges Bild und entspricht im Grunde genommen .mergeMap Mit maxConcurrency auf 1. In diesem Fall werden die Elemente wie oben bestellt, müssen es aber im Allgemeinen nicht sein so. Die einzige Garantie, die wir haben, ist, dass die Emission neuer Elemente nach ihrer Position im zugrunde liegenden Strom geordnet wird. Beispiel: [3,1,2,4,9,1] Und [2,3,1,1,9,4] Sind gültig, [1,1,4,2,3,9] Jedoch nicht (da 4 Nach 2 Im zugrunde liegenden Stream ausgegeben wurde) .

Ein paar Beispiele mit mergeMap:

// implement .map with .mergeMap
Rx.Observable.prototype.mapWithMergeMap = function(mapFn) {
  return this.mergeMap(x => Rx.Observable.of(mapFn(x)));
}

Rx.Observable.range(1, 3)
  .mapWithMergeMap(x => x * x)
  .subscribe(x => console.log('mapWithMergeMap', x))

// implement .filter with .mergeMap
Rx.Observable.prototype.filterWithMergeMap = function(filterFn) {
  return this.mergeMap(x =>
    filterFn(x) ?
    Rx.Observable.of(x) :
    Rx.Observable.empty()); // return no element
}

Rx.Observable.range(1, 3)
  .filterWithMergeMap(x => x === 3)
  .subscribe(x => console.log('filterWithMergeMap', x))

// implement .delay with .mergeMap 
Rx.Observable.prototype.delayWithMergeMap = function(delayMs) {
  return this.mergeMap(x =>
    Rx.Observable.create(obs => {
      // setTimeout is naive - one should use scheduler instead
      const token = setTimeout(() => {
        obs.next(x);
        obs.complete();
      }, delayMs)
      return () => clearTimeout(token);
    }))
}

Rx.Observable.range(1, 3)
  .delayWithMergeMap(500)
  .take(2)
  .subscribe(x => console.log('delayWithMergeMap', x))

// recursive count
const count = (from, to, interval) => {
  if (from > to) return Rx.Observable.empty();
  return Rx.Observable.timer(interval)
    .mergeMap(() =>
      count(from + 1, to, interval)
      .startWith(from))
}

count(1, 3, 1000).subscribe(x => console.log('count', x))

// just an example of bit different implementation with no returns
const countMoreRxWay = (from, to, interval) =>
  Rx.Observable.if(
    () => from > to,
    Rx.Observable.empty(),
    Rx.Observable.timer(interval)
    .mergeMap(() => countMoreRxWay(from + 1, to, interval)
      .startWith(from)))

const maxConcurrencyExample = () =>
  Rx.Observable.range(1,7)
    .do(x => console.log('emitted', x))
    .mergeMap(x => Rx.Observable.timer(1000).mapTo(x), 2)
    .do(x => console.log('processed', x))
    .subscribe()

setTimeout(maxConcurrencyExample, 3100)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.1/Rx.min.js"></script>
100
artur grzesiak

Mit .mergeMap() können Sie eine Observable höherer Ordnung zu einem einzigen Stream zusammenfassen. Zum Beispiel:

Rx.Observable.from([1,2,3,4])
  .map(i => getFreshApiData())
  .subscribe(val => console.log('regular map result: ' + val));

//vs

Rx.Observable.from([1,2,3,4])
  .mergeMap(i => getFreshApiData())
  .subscribe(val => console.log('mergeMap result: ' + val));

function getFreshApiData() {
  return Rx.Observable.of('retrieved new data')
    .delay(1000);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.0/Rx.js"></script>

In meiner Antwort auf diese andere Frage finden Sie eine ausführliche Erläuterung der .xxxMap() -Operatoren: Rxjs - Wie kann ich mehrere Werte in einem Array extrahieren und sie synchron an den beobachtbaren Datenstrom zurückgeben? =

20