flatMap, mergeMap, switchMap и concatMap в rxjs?

Кто-то, пожалуйста, объясните разницу между SwitchMap и FlatMap с точки зрения Javascript (в angular перспективе, rxjs 5)

В моем понимании.

SwitchMap генерирует только последнее наблюдаемое значение и отменяет предыдущее наблюдаемое.

flatMap собирает все отдельные наблюдаемые и возвращает все наблюдаемые в одном массиве, не заботясь о порядке наблюдаемых. работает асинхронно.

concatMap сохраняет порядок и выдает все наблюдаемые значения, работает синхронно

это правильно?

Чем MergeMap работает не так, как описано выше?

кто-то, пожалуйста, объясните на примере.

Ответ 1

@ZahiC, классный ответ - мне нравится использование функциональной композиции в примере кода. Если позволите, я хотел бы позаимствовать его, чтобы проиллюстрировать пару дополнительных точек с использованием временных наблюдений.

Наружный, внутренний и контрольный

Все эти операторы являются операторами преобразования, такими как map(), общей особенностью является то, что они имеют внешнюю и внутреннюю наблюдаемую. Ключевым отличием является то, как внешняя наблюдаемая контролирует внутреннюю наблюдаемую.

Чтобы сравнить их, мой пример кода запускает их попарно, выводя значения в виде [outerValue,innerValue]. Я добавил интервалы к тесту и изменил внутреннюю задержку, чтобы произошло некоторое перекрытие по времени (используется формула delay((5-x)*200)).


mergeMap vs concatMap

Они оба выводят все значения, разница в порядке.

mergeMap - порядок по внутренним наблюдаемым
[0,0], [1,0], [0,1], [2,0], [1,1], [3,0], [2,1], [4,0], [3, 1], [4,1]

concatMap - порядок по внешним наблюдаемым
[0,0], [0,1], [1,0], [1,1], [2,0], [2,1], [3,0], [3,1], [4, 0], [4,1]

Исходя из вывода, внешний вывод mergeMap может быть задержан в последовательности, но concatMap следует строгой последовательности внешнего излучения.


SwitchMap против выхлопной карты

Они оба дросселируют выход.

switchMap - дроссельная заслонка по последнему слову
[3,0], [4,0], [4,1]

выхлопная карта - сначала дроссельная заслонка
[0,0], [0,1], [4,0], [4,1]

Из выходных данных switchMap регулирует любые неполные внутренние выбросы, но выхлопные потоки следуют, пока не завершатся предыдущие.


mergeMap vs switchMap

Я добавил это, потому что switchmap часто используется в SO-ответах, где действительно следует использовать mergeMap.

mergeMap - порядок по внутренним наблюдаемым
[0,0], [1,0], [0,1], [2,0], [1,1], [3,0], [2,1], [4,0], [3, 1], [4,1]

switchMap - дроссельная заслонка по последнему слову
[3,0], [4,0], [4,1]

Основной вывод заключается в том, что выход switchMap является непредсказуемым в зависимости от времени внутренней наблюдаемой, например, если внутренняя часть представляет собой http get, результаты могут зависеть от скорости соединения.


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>

Ответ 2

Принимая это от предыдущего ответа:

  • flatMap/mergeMap - создает Observable немедленно для любого исходного элемента, все предыдущие Observables сохраняются в живых
  • concatMap - ожидает завершения предыдущего Наблюдаемого до создания следующего
  • switchMap - для любого исходного элемента завершает предыдущий Наблюдаемый и сразу создает следующий
  • exhaustMap - исходные элементы игнорируются, пока предыдущий Observable не завершен

Ниже приведен пример того, как каждый из операторов ведет себя, когда источником являются непосредственные элементы (0,1,2,3,4), а функция карты создает Observable, которая задерживает каждый элемент на 500 мсек:

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>

Ответ 3

На мраморной диаграмме ниже исходный поток, который излучает со скоростью 5 мс, 10 мс, 20 мс, будет * сопоставлен с timer(0, 3), ограничено 3 выбросами:

mergeMap vs exhaustMap vs switchMap vs concatMap

Поиграйте с этой мраморной диаграммой здесь: "mergeMap vs выхлопная карта против switchMap против concatMap"

Уже имея все эти удивительные ответы, я хотел бы добавить более наглядное объяснение

Надеюсь, это поможет кому-то

Ответ 4

Это в начале немного долго, чтобы понять, по крайней мере для меня.

В любом случае, подумайте:

flatMap ДРУГОЕ ИМЯ Для mergeMap - mergeMap допускается необязательный параметр concurrency, который определяет, сколько Observables могут быть подписаны одновременно

concatMap равно mergeMap с параллелизмом, установленным в 1

с mergeMap вы не теряете ни одного события, которое излучается Observables, которое вы объединяете, как вы предложили в своем ответе

switchMap работает так, как вы описали (см. эту статью для более подробной информации https://blog.angular-university.io/rxjs-switchmap-operator/)

Ответ 5

Я сделал небольшой пример/пример использования запрошенных операторов некоторое время назад.

https://stackblitz.com/edit/rxjs-map-operators

Вы можете выбрать интервал или щелкнуть для выдачи внешних наблюдаемых значений. Для внутренней наблюдаемой вы можете выбрать, отправлять ли интервал (3 элемента) или запрос http.

Это напечатает результаты ниже выбора.