Búsqueda de sitios web

javascript - flatMap, mergeMap, switchMap y concatMap en rxjs?


Alguien, por favor explique la diferencia entre SwitchMap y FlatMap en términos de Javascript (desde la perspectiva de Angular, rxjs 5)

Según mi comprensión.

SwitchMap solo emite el valor observable más reciente y cancela el observable anterior.

flatMap recopila todos los observables individuales y devuelve todos los observables en un solo arreglo sin preocuparse por el orden de los observables. Funciona de manera asincrónica.

concatMap preserva el orden y emite todos los valores observables, funciona de manera sincrónica

¿es eso correcto?

¿Cómo funciona mergeMap de manera diferente a lo anterior?

alguien, por favor explique con un ejemplo.

Respuesta #1

Tomando esto de una respuesta anterior:

  • flatMap/mergeMap - crea un Observable inmediatamente para cualquier elemento de origen, todos los Observables anteriores se mantienen activos. Nota: flatMap es un alias de mergeMap y flatMap será eliminado en RxJS 8.
  • concatMap - espera a que el Observable anterior se complete antes de crear el siguiente
  • switchMap - para cualquier elemento de origen, completa el Observable anterior y crea inmediatamente el siguiente
  • exhaustMap - los elementos de la fuente se ignoran mientras el Observable previo no se ha completado

Aquí hay un ejemplo de cómo se comporta cada uno de los operadores cuando la fuente son elementos inmediatos (0,1,2,3,4) y la función map crea un Observable que retrasa cada elemento en 400, 100, 500, 200 y 300 ms:

const { mergeMap, flatMap, concatMap, switchMap, exhaustMap } = Rx.operators;

const example = operator => () =>
  Rx.Observable.from([0,1,2,3,4])
  .pipe(
    operator(x => Rx.Observable.of(x).delay(500))
  )
  .subscribe(console.log, () => {}, () => console.log(`${operator.name} completed`));

const mm = example(mergeMap);
const fm = example(flatMap);
const cm = example(concatMap);    
const sm = example(switchMap);
const em = example(exhaustMap);
.examples > div {
  cursor: pointer;
  background-color: #4CAF50;
  color: white;
  padding: 7px 16px;
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.8/Rx.js"></script>

<div class='examples'>
  <div onClick='mm()'>mergeMap </div>
  <div onClick='fm()'>flatMap</div>
  <div onClick='cm()'>concatMap</div>
  <div onClick='sm()'>switchMap</div>
  <div onClick='em()'>exhaustMap</div>
</div>

Respuesta #2

En el diagrama de mármol a continuación, un flujo de origen que emite a 5ms, 10ms, 20ms será *Mapeado a un timer(0, 3), limitado a 3 emisiones:

Juega con este diagrama de mármol aquí: "mergeMap vs exhaustMap vs switchMap vs concatMap"

Ya teniendo todas estas respuestas increíbles, quería agregar una explicación más visual

Espero que le ayude a alguien

Respuesta #3

@ZahiC, respuesta genial: me gusta el uso de la composición funcional en el ejemplo de código. Si me permites, me gustaría tomarlo prestado para ilustrar un par de puntos adicionales usando observables temporizados.

Externo, interno y control

Estos operadores son todos operadores de transformación como map(), la característica común es que tienen un observable externo y un observable interno. La diferencia clave es la manera en que el observable externo controla el observable interno.

Para contrastarlos, mi ejemplo de código los ejecuta en pares, mostrando los valores en la forma [outerValue,innerValue]. He añadido intervalos a la prueba y cambiado el retraso interno para que haya cierta superposición en el tiempo (la fórmula utilizada es delay((5-x)*200)).


mergeMap vs concatMap

Ambos producen todos los valores, la diferencia es el orden.

mergeMap - Ordenar por observables internos
[0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4,0],[3,1],[4,1]

concatMap - Ordenar por observador externo
[0,0],[0,1],[1,0],[1,1],[2,0],[2,1],[3,0],[3,1],[4,0],[4,1]

A partir del resultado, la emisión externa de mergeMap puede retrasarse en la secuencia, pero concatMap sigue estrictamente la secuencia de emisión externa.


switchMap vs exhaustMap

Ambos estrangulan la salida.

switchMap - Regular por último
[3,0],[4,0],[4,1]

exhaustMap - Estrangular por el primero
[0,0],[0,1],[4,0],[4,1]

A partir del resultado, switchMap limita cualquier emisión incompleta interna, pero exhaustMap limita las emisiones posteriores hasta que las anteriores se completen.


mergeMap vs switchMap

Incluí esto porque switchMap a menudo se usa en respuestas de SO donde realmente debería usarse mergeMap.

mergeMap - Ordenar por observables internos
[0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4,0],[3,1],[4,1]

switchMap - Regular por último
[3,0],[4,0],[4,1]

La conclusión principal es que la salida de switchMap es impredecible dependiendo del momento del observable interno, por ejemplo, si el interno es un http get los resultados pueden depender de la velocidad de la conexión.


console.clear()
const { mergeMap, flatMap, concatMap, switchMap, exhaustMap, delay, map, take, toArray } = Rx.operators;

const note = {
  mergeMap:  'Order by inner observable', 
  concatMap: 'Order by outer observable', 
  switchMap: 'Throttle by last', 
  exhaustMap: 'Throttle by first', 
}
const title = (operator) => {
  const opName = operator.name.replace('$1','')
  return `${opName} - ${note[opName]}`
}
const display = (x) => {
  return map(y => `[${x},${y}]`)
}
const inner = (x) => Rx.Observable.timer(0,500)
.pipe(
  delay((5-x)*200),
  display(x),
  take(2)
)

const example = operator => () => {
  Rx.Observable.interval(500).take(5)
  .pipe(
    operator(x => inner(x)),
    toArray(),
    map(vals => vals.join(','))
  )
  .subscribe(x => {
    console.log(title(operator))
    console.log(x)
  });
};

const run = (fn1, fn2) => {
  console.clear()
  fn1()
  fn2()
}
const mmVcm = () => run(example(mergeMap), example(concatMap));
const smVem = () => run(example(switchMap), example(exhaustMap));
const mmVsm = () => run(example(mergeMap), example(switchMap));
.examples > div {
  cursor: pointer;
  background-color: #4CAF50;
  color: white;
  padding: 7px 16px;
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.8/Rx.js"></script>

<div class='examples'>
  <div onClick='mmVcm()'>mergeMap vs concatMap </div>
  <div onClick='smVem()'>switchMap vs exhaustMap</div>
  <div onClick='mmVsm()'>mergeMap vs switchMap </div>
</div>

Respuesta #4

Supongamos que estás suscrito a un canal del clima. El locutor del clima lee el informe que se le pasó después de pasar por varias operaciones.

  • Si el presentador está leyendo un informe y mientras lee otro informe llega. Si él deja de leer el primer informe y comienza a leer el nuevo informe tan pronto como llega, entonces está haciendo switchMap. Porque switchMap proyecta cada valor de origen a un observable que se fusiona en el observable de salida, emitiendo valores solo del observable proyectado más "recientemente".

  • si el locutor de radio no comienza un nuevo informe hasta que el primero haya terminado, tenemos concatMap. concatMap proyecta cada valor de origen a un observable que se combina en el observable de salida de manera serializada, esperando a que cada uno se complete antes de combinar el siguiente.

  • si llega un nuevo informe mientras el presentador todavía está leyendo y su respuesta de alguna manera es leer ambos al mismo tiempo, entonces tenemos mergeMap/flatMap. (flatMap es un alias de mergeMap). Porque mergeMap proyecta cada valor de la fuente a un observable que se mezcla en el observable de salida. "mergeMap" es una versión más básica de switchMap y concatMap.

Respuesta #5

Aquí hay una forma más de pensar sobre la diferencia entre los distintos tipos de mapas. Esto me ayudó a comprenderlo. Espero que pueda ayudar a otros.

Considere las siguientes fuentes:

  • Una fuente "alfa" que produce letras minúsculas del alfabeto: a, b, c y d
  • 4 fuentes separadas de "palabras", cada una produciendo 3 palabras que comienzan con una letra particular del alfabeto - a, b, c o d - y luego completando

Para ilustrar la diferencia entre diferentes tipos de mapas, vincularemos elementos de la fuente del alfabeto a sus fuentes de "palabra" correspondientes a esa letra del alfabeto, usando cada mapa diferente para ver los diferentes resultados.

Mapa

Esto es diferente de los otros mapas porque no introduce otra fuente de observable. Simplemente transforma un valor entrante en otro valor.

Entonces, la salida de la fuente en minúsculas, pasando por un Map que transforma la entrada a mayúsculas, sería simplemente:

Input: a,b,c,d

Output: A, B, C, D

Mapa de Cambio

Esto transforma cada entrada en otra fuente, cambiando la salida para que provenga de esa nueva fuente (es decir, suscribiéndose a esa nueva fuente). Cuando llega otra entrada "alpha", la fuente "word" cambia (nos damos de baja de la fuente "word" anterior).

Input: a,b,c,d

Output: animal, aardvark, bull, baker, beach, cow, dog, day, dinner

Mapa de Concat

Como switchMap, excepto que Concat espera hasta que cada fuente se complete antes de pasar a la siguiente.

Input: a,b,c,d

Output: animal, aardvark, axe, bull, baker, beach, cow, car, cat, dog, day, dinner

Mapa de Escape

Como Concat Map, excepto que ignorará cualquier entrada que llegue mientras aún está completando la última fuente. El ejemplo a continuación asume que las entradas "alpha" "b" y "d" llegaron mientras la fuente mapeada anterior aún se estaba completando, por lo que fueron ignoradas.

Input: a,b,c,d

Output: animal, aardvark, axe, cow, car, cat

Mapa de Fusión (también conocido como Mapa Plano)

Como concatMap en que cada fuente se ejecuta hasta completarse, pero una nueva fuente puede comenzar mientras otras fuentes aún están en curso, por lo que las secuencias se superponen.

Input: a,b,c,d

Output: animal, aardvark, bull, axe, baker, cow, car, beach, dog, day, cat, dinner

Artículos relacionados