Почему нам нужно использовать flatMap?


94

Я начинаю использовать RxJS и не понимаю, почему в этом примере нам нужно использовать такую ​​функцию, как flatMapили concatAll; где здесь массив массивов?

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

Если кто-то сможет наглядно объяснить происходящее, это будет очень полезно.


1
этот ответ хорош из-за ценных ссылок, которые он предоставляет, но терминология rxjs плохо переводится на английский язык. (картинки лучше). Вот почему я рекомендую вместо этого запускать простые примеры, подобные этому, или более сложные примеры в репозитории rxjs и добавлять операторы «.do» до и после оператора flatmap и map, а затем просто устанавливать точку останова с помощью отладчика Chrome. вы сразу увидите, что каждый из них производит разный результат
HipsterZipster

5
Думаю, если flatMapбы назвали mapThenFlatten, то было бы меньше путаницы.
goat

Ответы:


73

Когда я начал смотреть, Rxjsя тоже наткнулся на этот камень. Мне помогло следующее:

  • документация с reactivex.io. Например, для flatMap: http://reactivex.io/documentation/operators/flatmap.html
  • документация от rxmarbles: http://rxmarbles.com/ . Вы не найдете flatMapтам, вы должны посмотреть mergeMapвместо этого (другое имя).
  • введение в Rx, которое вам не хватало: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Он обращается к очень похожему примеру. В частности, он касается того факта, что обещание сродни наблюдаемому, испускающему только одно значение.
  • наконец, глядя на информацию о типе из RxJava. Не набираемый Javascript здесь не помогает. В основном if Observable<T>обозначает наблюдаемый объект, который передает значения типа T, затем flatMapпринимает функцию типа в T' -> Observable<T>качестве аргумента и возвращает Observable<T>. mapпринимает функцию типа T' -> Tи возвращает Observable<T>.

    Возвращаясь к вашему примеру, у вас есть функция, которая производит обещания из строки URL. Итак T' : string, и T : promise. И из того, что мы сказали ранее promise : Observable<T''>, так что T : Observable<T''>с T'' : html. Если вы поместите эту функцию создания обещаний map, вы получите Observable<Observable<T''>>то, что вам нужно Observable<T''>: вы хотите, чтобы наблюдаемый объект испускал htmlзначения. flatMapназывается так, потому что он сглаживает (удаляет наблюдаемый слой) результат из map. В зависимости от вашего опыта, это может быть китайский язык, но мне все стало кристально ясно после ввода информации и рисунка отсюда: http://reactivex.io/documentation/operators/flatmap.html .


2
Я забыл упомянуть , что вы должны быть в состоянии упростить return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));в return jQuery.getJSON(requestUrl);качестве flatMapтакже принимает функцию селектора , которая возвращает обещание т.е. функцию типа T' -> Promise.
user3743222 02

2
Вау, этот GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) чертовски фантастичен. Я рекомендую его всем, кто работает с любыми библиотеками ReactiveX, такими как RxJS.
Джейкоб Штамм

@JacobStamm, я согласен. Просто упрощает жизнь.
CruelEngine

Что делает этот синтаксис среднее: T’ -> T? Я понимаю, что Tэто общее, но что такое апостроф и не жирная стрелка?
1252748

Вы можете заменить T 'на X или Y, не меняя смысла нигде в ответе. Стрелка - это обозначение Haskell для сигнатуры типа. Итак, T '-> T - это подпись функции, которая принимает элемент типа T' и возвращает элемент типа 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]]

Вы используете flatMap, когда у вас есть Observable, результаты которого больше Observable.

Если у вас есть наблюдаемое, которое создается другим наблюдаемым, вы не можете фильтровать, уменьшать или отображать его напрямую, потому что у вас есть Observable, а не данные. Если вы создаете наблюдаемое, выбирайте flatMap вместо map; тогда с тобой все в порядке.

Как и во втором фрагменте, если вы выполняете асинхронную операцию, вам необходимо использовать 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>


34

Люди склонны чрезмерно усложнять вещи , давая определение, которое гласит:

flatMap преобразует элементы, излучаемые Observable, в Observable, а затем сглаживает выбросы от них в один Observable

Клянусь, это определение до сих пор меня смущает, но я объясню его самым простым способом, используя пример

Наша ситуация : у нас есть наблюдаемый объект, который возвращает данные (простой URL-адрес), который мы собираемся использовать для выполнения HTTP-вызова, который вернет наблюдаемый объект, содержащий нужные нам данные, поэтому вы можете визуализировать ситуацию следующим образом:

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

поэтому, как вы можете видеть, мы не можем напрямую получить данные, которые нам нужны, поэтому первый способ получить данные мы можем использовать обычные подписки, например:

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

это работает, но, как вы можете видеть, мы должны вложить подписки, чтобы получить наши данные, в настоящее время это неплохо, но представьте, что у нас есть 10 вложенных подписок, обслуживание которых станет невозможным.

поэтому лучший способ справиться с этим - просто использовать оператор, flatMapкоторый будет делать то же самое, но заставляет нас избегать этой вложенной подписки:

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

32

flatMap преобразовать элементы, излучаемые Observable, в новые Observable, а затем сгладить выбросы из них в один Observable.

Ознакомьтесь с приведенным ниже сценарием, где get("posts")возвращается Observable, который "сглаживается" 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.

2
Хороший, простой ответ. Думаю, это может быть лучше всего.
vaughan

«flatMap преобразует элементы, излучаемые Observable, в новые Observable, а затем сглаживает выбросы от них в один Observable». Это отличный материал.
MBak

18

Просто:

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

16

Это не массив массивов. Это наблюдаемое из наблюдаемых.

Следующее возвращает наблюдаемый поток строки.

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

Хотя это возвращает наблюдаемый поток наблюдаемого потока json

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

flatMap автоматически сглаживает наблюдаемое, чтобы мы могли непосредственно наблюдать за потоком json


3
Трудно понять эту концепцию, не могли бы вы добавить комментарии к визуальному элементу, который вы имеете в виду «возвращает наблюдаемый поток наблюдаемого потока json». Спасибо.
user233232 02

@ user233232, например, от [x, x, x, x] до [[xxx], [[xxx], [xxx]]]]
serkan

Ключом к пониманию первого предложения является понимание того, что flatMapmap) не относятся к массивам. Эти операции можно определить в любом универсальном контейнере или оболочке, включая массивы, словари, «необязательные элементы», реактивные потоки, обещания, указатели и даже сами функции. Это новое свойство математической конструкции, называемой монадой. Все приведенные выше примеры соответствуют требованиям для монады, поэтому всем им можно дать определение mapи flatMap(с некоторыми оговорками).
mklbtz

14

Здесь показана эквивалентная реализация flatMap с использованием подписок.

Без flatMap:

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

С помощью 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

Надеюсь, это поможет.

Оливье.


13

Observable - это объект, который генерирует поток событий: Next, Error и Completed.

Когда ваша функция возвращает Observable, она возвращает не поток, а экземпляр Observable. flatMapОператор просто переводит этот экземпляр в поток.

Это поведение по flatMapсравнению с map: Выполнить данную функцию и преобразовать полученный объект в поток.


7

С помощью 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)})

Без 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)})
})

0

flatMap преобразует элементы, излучаемые Observable, в Observable, а затем сглаживает выбросы от них в один Observable

Я не дурак, но мне пришлось прочитать это 10 раз, но до сих пор не понимаю. Когда я читаю фрагмент кода:

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

тогда я мог понять, что происходит, он делает две вещи:

flatMap :

  1. map : transform *) испускает элементы в Observables.
  2. плоский : затем объедините эти Observable как один Observable.

*) Слово преобразования говорит, что предмет может быть преобразован во что-нибудь еще.

Тогда становится ясно, что оператор слияния выполняет сглаживание без отображения. Почему бы не назвать это mergeMap ? Похоже, что для flatMap также существует Alias mergeMap с таким именем .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.