Je ne comprends pas le but de mergeMap
du tout. J'ai entendu deux "explications:
merge
et map
" - nope (ou je ne peux pas le répliquer).Considérons le code suivant:
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)
)
La dernière pièce intitulée "Qui sait quoi" ne fait rien de plus qu'une carte sur obs1
- à quoi ça sert?
Que fait réellement mergeMap
? Qu'est-ce qu'un exemple de cas d'utilisation valide? (De préférence avec du code)
Articles qui ne m'ont pas du tout aidé (le code mergeMap ci-dessus est issu de l'un de ceux-ci): 1 , 2
tl; dr;mergeMap
est bien plus puissant que map
. Comprendre mergeMap
est la condition nécessaire pour accéder à la pleine puissance de Rx.
mergeMap
et map
agissent tous deux sur un seul flux (vs Zip
, combineLatest
)
mergeMap
et map
peuvent tous deux transformer des éléments d'un flux (vs filter
, delay
)
ne peut pas changer la taille du flux source (hypothèse: map
elle-même ne throw
); pour chaque élément de la source, exactement un mapped
élément est émis; map
ne peut pas ignorer les éléments (comme par exemple filter
);
dans le cas du planificateur par défaut, la transformation se fait de manière synchrone; être clair à 100%: le flux source peut fournir ses éléments de manière asynchrone, mais chaque élément suivant est immédiatement mapped
et réémis; map
ne peut pas décaler des éléments dans le temps, comme par exemple delay
aucune restriction sur les valeurs de retour
id
: x => x
peut changer la taille du flux source; pour chaque élément, il peut y avoir un nombre arbitraire (0, 1 ou plusieurs) de nouveaux éléments créés/émis
il offre un contrôle total sur l'asynchronicité - à la fois lorsque de nouveaux éléments sont créés/émis et combien d'éléments du flux source doivent être traités simultanément. par exemple, supposons que le flux source émette 10 éléments mais que maxConcurrency
soit défini sur 2, les deux premiers éléments seront traités immédiatement et les 8 autres éléments mis en mémoire tampon; une fois que l'un des complete
d traités, le prochain élément du flux source sera traité, etc. - c'est un peu compliqué, mais regardez l'exemple ci-dessous
tous les autres opérateurs peuvent être implémentés avec seulement mergeMap
et Observable
constructeur
peut être utilisé pour des opérations asynchrones récursives
les valeurs de retour doivent être de type Observable (ou Rx doit savoir comment en créer une observable - par exemple, une promesse, un tableau)
id
: x => Rx.Observable.of(x)
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]
L'analogie ne montre pas l'image complète et correspond en gros à .mergeMap
Avec maxConcurrency
défini sur 1. Dans un tel cas, les éléments seront ordonnés comme ci-dessus, mais dans le cas général cela ne doit pas nécessairement l'être alors. La seule garantie dont nous disposons est que l'émission de nouveaux éléments sera ordonnée en fonction de leur position dans le flux sous-jacent. Par exemple: [3,1,2,4,9,1]
Et [2,3,1,1,9,4]
Sont valides, mais [1,1,4,2,3,9]
Ne l'est pas (puisque 4
A été émis après 2
Dans le flux sous-jacent) .
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>
.mergeMap()
vous permet d'aplatir un observable d'ordre supérieur en un seul flux. Par exemple:
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>
Voir ma réponse à cette autre question pour une explication détaillée des opérateurs .xxxMap()
: Rxjs - Comment puis-je extraire plusieurs valeurs à l'intérieur d'un tableau et les renvoyer au flux observable de manière synchrone =