Pourquoi avons-nous besoin d'utiliser flatMap?

92

Je commence à utiliser RxJS et je ne comprends pas pourquoi dans cet exemple nous devons utiliser une fonction comme flatMapou concatAll; où est le tableau de tableaux ici?

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)})

Si quelqu'un peut expliquer visuellement ce qui se passe, ce sera très utile.

utilisateur233232
la source
1
cette réponse est excellente en raison des références précieuses qu'elle fournit, mais la terminologie rxjs ne se traduit pas bien en anglais. (les images sont meilleures). C'est pourquoi je recommande plutôt d'exécuter des exemples simples comme celui-ci, ou des exemples plus complexes dans le repo rxjs et d'ajouter des opérateurs ".do" avant et après un opérateur flatmap et map, puis simplement définir un point d'arrêt avec le débogueur Chrome. vous verrez instantanément que chacun produit une sortie différente
HipsterZipster
5
Je pense que si flatMapcela avait été nommé mapThenFlatten, alors ce serait moins déroutant.
chèvre

Réponses:

73

Quand j'ai commencé à y jeter un œil, Rxjsje suis également tombé sur cette pierre. Ce qui m'a aidé est le suivant:

  • documentation de reactivex.io. Par exemple, pour flatMap: http://reactivex.io/documentation/operators/flatmap.html
  • documentation de rxmarbles: http://rxmarbles.com/ . Vous ne trouverez pas flatMaplà, vous devez regarder à la mergeMapplace (un autre nom).
  • l'introduction à Rx qui vous manquait: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Il aborde un exemple très similaire. Il aborde en particulier le fait qu'une promesse s'apparente à un observable n'émettant qu'une seule valeur.
  • enfin en regardant les informations de type de RxJava. Javascript ne pas être tapé n'aide pas ici. Fondamentalement, si Observable<T>désigne un objet observable qui pousse des valeurs de type T, flatMapprend alors une fonction de type T' -> Observable<T>comme argument et retourne Observable<T>. mapprend une fonction de type T' -> Tet retourne Observable<T>.

    Pour revenir à votre exemple, vous avez une fonction qui produit des promesses à partir d'une chaîne d'URL. Alors T' : string, et T : promise. Et d'après ce que nous avons dit auparavant promise : Observable<T''>, oui T : Observable<T''>, avec T'' : html. Si vous mettez cette fonction de production de promesse map, vous obtenez Observable<Observable<T''>>quand ce que vous voulez est Observable<T''>: vous voulez que l'observable émette les htmlvaleurs. flatMapest appelé comme ça car il aplatit (supprime une couche observable) du résultat map. Selon votre parcours, cela peut être chinois pour vous, mais tout est devenu clair pour moi avec les informations de frappe et le dessin d'ici: http://reactivex.io/documentation/operators/flatmap.html .

user3743222
la source
2
J'ai oublié de mentionner que vous devriez pouvoir simplifier return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));en return jQuery.getJSON(requestUrl);car flatMapaccepte également une fonction de sélection qui renvoie une promesse c'est-à-dire une fonction de type T' -> Promise.
user3743222
2
Wow, ce GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) est vraiment fantastique. Je le recommande à toute personne travaillant avec des bibliothèques ReactiveX comme RxJS.
Jacob Stamm
@JacobStamm je suis d'accord. Simplifie simplement les choses.
CruelEngine
Qu'est - ce que cela signifie de syntaxe: T’ -> T? Je comprends le Tcomme un générique, mais quelle est l'apostrophe et la flèche non grasse?
1252748
Vous pouvez remplacer T 'par X ou Y sans changer la signification n'importe où dans la réponse. La flèche est la notation Haskell pour la signature de type. Donc T '-> T est la signature d'une fonction qui prend un élément de type T' et renvoie un élément de type T
user3743222
124
['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]]

Vous utilisez flatMap lorsque vous avez un observable dont les résultats sont plus observables.

Si vous avez une observable qui est produite par une autre observable, vous ne pouvez pas la filtrer, la réduire ou la mapper directement car vous avez une observable et non les données. Si vous produisez une observable, choisissez flatMap sur map; alors tu vas bien.

Comme dans le deuxième extrait de code, si vous effectuez une opération asynchrone, vous devez utiliser flatMap.

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>

Serkan
la source
31

flatMap transformer les éléments émis par un observable en nouveaux observables, puis aplatir les émissions de ceux-ci en un seul observable.

Découvrez le scénario ci-dessous où get("posts")renvoie un observable qui est «aplati» par 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.
Emmanuel Osimosu
la source
2
Belle réponse simple. Je pense que c'est peut-être le meilleur.
vaughan
"flatMap transforme les éléments émis par un observable en nouveaux observables, puis aplatit les émissions de ceux-ci en un seul observable." Ce sont d'excellents trucs.
MBak
31

Les gens ont tendance à trop compliquer les choses en donnant la définition qui dit:

flatMap transforme les éléments émis par un observable en observables, puis aplatit les émissions de ceux-ci en un seul observable

Je jure que cette définition me trouble encore mais je vais l'expliquer de la manière la plus simple qui soit en utilisant un exemple

Notre situation : nous avons une observable qui renvoie des données (URL simple) que nous allons utiliser pour faire un appel HTTP qui renverra une observable contenant les données dont nous avons besoin afin que vous puissiez visualiser la situation comme ceci:

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

comme vous pouvez le voir, nous ne pouvons pas accéder directement aux données dont nous avons besoin, donc la première façon de récupérer les données nous permet d'utiliser uniquement des abonnements normaux comme celui-ci:

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

cela fonctionne mais comme vous pouvez le voir, nous devons imbriquer des abonnements pour obtenir nos données, cela n'a pas l'air mal pour le moment, mais imaginez que nous avons 10 abonnements imbriqués qui deviendraient non maintenables.

donc une meilleure façon de gérer cela est simplement d'utiliser l'opérateur flatMapqui fera la même chose mais nous évite cet abonnement imbriqué:

Observable_1
    .flatMap(URL => Http.get(URL))
    .subscribe(Data_We_Need => console.log(Data_We_Need));
Hamed Baatour
la source
19

Facile:

[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]]
drpicox
la source
16

Ce n'est pas un tableau de tableaux. C'est un observable d'observable (s).

Ce qui suit renvoie un flux de chaîne observable.

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

Bien que cela renvoie un flux observable de flux observable de json

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

flatMap aplatit l'observable automatiquement pour nous afin que nous puissions observer directement le flux json

Lucius
la source
3
Il est difficile de comprendre ce concept, pouvez-vous s'il vous plaît ajouter des commentaires à visuel ce que vous voulez dire "renvoie un flux observable de flux observable de json". Merci.
user233232
@ user233232, comme [x, x, x, x] à [[xxx], [[xxx], [xxx]]]
serkan
La clé pour comprendre la première phrase est de comprendre que flatMap(et map) ne sont pas spécifiques aux tableaux. Il est possible de définir ces opérations sur n'importe quel conteneur ou wrapper générique, y compris les tableaux, les dictionnaires, les "optionnels", les flux réactifs, les promesses, les pointeurs et même les fonctions elles-mêmes. C'est une propriété émergente d'une construction mathématique appelée la monade. Tous les exemples ci-dessus remplissent les conditions requises pour être une monade, et ils peuvent donc tous recevoir une définition de mapet a flatMap(avec quelques mises en garde).
mklbtz
14

Ici pour montrer l'implémentation équivalente d'un flatMap utilisant des abonnés

Sans flatMap:

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

Avec 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

J'espère que cela pourrait aider.

Olivier.

Olivier Cherrier
la source
13

Un observable est un objet qui émet un flux d'événements: suivant, erreur et terminé.

Lorsque votre fonction renvoie un Observable, elle ne renvoie pas un flux, mais une instance de Observable. L' flatMapopérateur mappe simplement cette instance à un flux.

C'est le comportement de flatMappar rapport à map: Exécuter la fonction donnée et aplatir l'objet résultant dans un flux.

AkkarinZA
la source
7

Avec 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)})

Sans 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)})
})
Thanawat
la source
0

flatMap transforme les éléments émis par un observable en observables, puis aplatit les émissions de ceux-ci en un seul observable

Je ne suis pas stupide mais j'ai dû lire ceci 10 fois et je ne comprends toujours pas. Quand j'ai lu l'extrait de code:

[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]

alors je pourrais comprendre ce qui se passe, ça fait deux choses:

flatMap :

  1. map : transformer *) les éléments émis en observables.
  2. flat : puis fusionnez ces observables en un seul observable.

*) Le mot de transformation indique que l'élément peut être transformé en autre chose.

Ensuite, l' opérateur de fusion devient clair, il effectue l'aplatissement sans le mappage. Pourquoi ne pas l'appeler mergeMap ? Il semble qu'il existe également un Alias mergeMap avec ce nom pour flatMap .

Herman Van Der Blom
la source