Чому нам потрібно використовувати flatMap?


92

Я починаю використовувати RxJS, і я не розумію, чому в цьому прикладі нам потрібно використовувати функцію типу flatMapor 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" до та після оператора плоскої карти та картографії, а потім просто встановити точку зупинки за допомогою налагоджувача Chrome. Ви миттєво побачите, що кожен видає різні результати
HipsterZipster

5
Думаю, якби flatMapбуло б названо mapThenFlatten, то це було б менш заплутаним.
козел

Відповіді:


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 тут не допомагає. В основному, якщо Observable<T>позначає спостережуваний об'єкт, який штовхає значення типу T, тоді flatMapприймає функцію type 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

2
Ого, цей GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) криваво фантастичний. Я рекомендую його всім, хто працює з будь-якими бібліотеками ReactiveX, такими як RxJS.
Jacob Stamm

@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.

Якщо у вас є спостережуване, яке створюється іншим спостережуваним, ви не можете фільтрувати, зменшувати або відображати його безпосередньо, оскільки у вас є спостережуваний не дані. Якщо ви створюєте спостережуваний, виберіть flatMap над картою; то ти в порядку.

Як і у другому фрагменті, якщо ви виконуєте асинхронну операцію, вам потрібно використовувати 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>


31

flatMap перетворити елементи, випромінювані спостережуваним, у нові спостережувані, а потім вирівнює викиди від них в єдиний спостережуваний.

Ознайомтесь із сценарієм нижче, де 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
Приємна, проста відповідь. Я думаю, що це може бути найкращим.
воган,

"flatMap перетворює елементи, що випромінюються Observable, на нові Observables, а потім згладжує викиди з них в єдиний Observable." Це чудова штука.
MBak

31

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

flatMap перетворює елементи, що випромінюються Observable, у Observables, а потім згладжують викиди від них в єдиний 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));

19

Просто:

[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, вона повертає не потік, а екземпляр 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, у Observables, а потім згладжують викиди від них в єдиний 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 : перетворити *) випущені елементи в Observables.
  2. плоский : потім об’єднайте ці спостережувані як одне спостережуване.

*) Слово перетворення говорить, що предмет може бути перетворений в щось інше.

Тоді оператору злиття стає зрозуміло, він робить згладжування без відображення. Чому б не назвати це mergeMap ? Здається, існує також псевдонім mergeMap з такою назвою flatMap .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.