Mengapa kita perlu menggunakan flatMap?


94

Saya mulai menggunakan RxJS dan saya tidak mengerti mengapa dalam contoh ini kita perlu menggunakan fungsi seperti flatMapatau concatAll; di mana letak array di sini?

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

Jika seseorang dapat menjelaskan secara visual apa yang terjadi, itu akan sangat membantu.


1
jawaban ini bagus karena referensi berharga yang diberikannya, tetapi terminologi rxjs tidak diterjemahkan dengan baik ke dalam bahasa Inggris. (gambar lebih baik). Itulah mengapa saya merekomendasikan untuk menjalankan contoh sederhana seperti ini, atau contoh yang lebih kompleks di repo rxjs dan menambahkan operator ".do" sebelum dan sesudah operator peta dan peta datar, lalu cukup setel titik putus dengan debugger Chrome. Anda akan langsung melihat bahwa masing-masing menghasilkan keluaran yang berbeda
HipsterZipster

5
Saya pikir jika flatMapakan diberi nama mapThenFlatten, maka akan kurang membingungkan.
kambing

Jawaban:


73

Ketika saya mulai melihat-lihat, Rxjssaya juga tersandung pada batu itu. Yang membantu saya adalah sebagai berikut:

  • dokumentasi dari reactivex.io. Misalnya, untuk flatMap: http://reactivex.io/documentation/operators/flatmap.html
  • dokumentasi dari rxmarbles: http://rxmarbles.com/ . Anda tidak akan menemukannya di flatMapsana, Anda harus melihatnyamergeMap sebagai gantinya (nama lain).
  • pengantar Rx yang telah Anda lewatkan: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Ini membahas contoh yang sangat mirip. Secara khusus ini membahas fakta bahwa sebuah janji mirip dengan yang dapat diamati yang hanya memancarkan satu nilai.
  • akhirnya melihat informasi tipe dari RxJava. Javascript yang tidak diketik tidak membantu di sini. Pada dasarnya jika Observable<T>menunjukkan objek yang dapat diamati yang mendorong nilai tipe T, maka flatMapmengambil fungsi tipe T' -> Observable<T>sebagai argumennya, dan mengembalikan Observable<T>. mapmengambil fungsi tipe T' -> Tdan pengembalian Observable<T>.

    Kembali ke contoh Anda, Anda memiliki fungsi yang menghasilkan promise dari string url. Jadi T' : string, dan T : promise. Dan dari apa yang kami katakan sebelumnya promise : Observable<T''>, jadi T : Observable<T''>, dengan T'' : html. Jika Anda memasukkan fungsi yang menghasilkan janji itu map, Anda mendapatkan Observable<Observable<T''>>apa yang Anda inginkan Observable<T''>: Anda ingin yang dapat diamati memancarkan htmlnilai. flatMapdisebut seperti itu karena meratakan (menghilangkan lapisan yang dapat diamati) hasil dari map. Bergantung pada latar belakang Anda, ini mungkin bahasa Cina untuk Anda, tetapi semuanya menjadi sangat jelas bagi saya dengan mengetik info dan gambar dari sini: http://reactivex.io/documentation/operators/flatmap.html .


2
Saya lupa menyebutkan bahwa Anda harus dapat menyederhanakan return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));untuk return jQuery.getJSON(requestUrl);sebagai flatMapjuga menerima fungsi pemilih yang mengembalikan janji yaitu fungsi dari jenis T' -> Promise.
pengguna3743222

2
Wow, GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) itu sangat fantastis. Saya merekomendasikannya kepada siapa pun yang bekerja dengan pustaka ReactiveX seperti RxJS.
Jacob Stamm

@JobStamm saya setuju. Hanya membuat segalanya lebih mudah.
CruelEngine

Apa sintaks ini berarti: T’ -> T? Saya memahami itu Tsebagai generik, tapi apa itu tanda kutip dan panah tidak gemuk?
1252748

Anda dapat mengganti T 'dengan X atau Y tanpa mengubah artinya di mana pun dalam jawaban. Panahnya adalah notasi Haskell untuk tanda tangan tipe. Jadi T '-> T adalah tanda tangan untuk fungsi yang mengambil elemen tipe T' dan mengembalikan elemen tipe 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]]

Anda menggunakan flatMap saat Anda memiliki Observable yang hasilnya lebih banyak Observable.

Jika Anda memiliki observable yang diproduksi oleh observable lain, Anda tidak dapat memfilter, mengurangi, atau memetakannya secara langsung karena Anda memiliki Observable bukan datanya. Jika Anda menghasilkan yang dapat diamati, pilih flatMap di atas peta; maka kamu baik-baik saja.

Seperti pada potongan kedua, jika Anda melakukan operasi asinkron, Anda perlu menggunakan 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

Orang cenderung memperumit banyak hal dengan memberikan definisi yang mengatakan:

flatMap mengubah item yang dipancarkan oleh Observable menjadi Observable, lalu meratakan emisi tersebut menjadi satu Observable

Saya bersumpah definisi ini masih membingungkan saya tetapi saya akan menjelaskannya dengan cara yang paling sederhana yaitu dengan menggunakan contoh

Situasi Kami : kami memiliki sebuah observable yang mengembalikan data (URL sederhana) yang akan kami gunakan untuk membuat panggilan HTTP yang akan mengembalikan sebuah observable berisi data yang kami butuhkan sehingga Anda dapat memvisualisasikan situasi seperti ini:

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

sehingga seperti yang Anda lihat, kami tidak dapat menjangkau data yang kami butuhkan secara langsung sehingga cara pertama untuk mengambil data kami dapat menggunakan langganan biasa seperti ini:

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

ini berfungsi tetapi seperti yang Anda lihat, kami harus menumpuk langganan untuk mendapatkan data kami, saat ini ini tidak terlihat buruk, tetapi bayangkan kami memiliki 10 langganan bersarang yang akan menjadi tidak dapat dikelola.

jadi cara yang lebih baik untuk menangani ini adalah dengan menggunakan operator flatMapyang akan melakukan hal yang sama tetapi membuat kita menghindari langganan bersarang itu:

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

32

flatMap mengubah item yang dipancarkan oleh Observable menjadi Observable baru, lalu meratakan emisi tersebut menjadi satu Observable.

Lihat skenario di bawah ini di mana get("posts")mengembalikan Observable yang "diratakan" oleh 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
Jawaban yang bagus dan sederhana. Saya pikir ini mungkin yang terbaik.
vaughan

"flatMap mengubah item yang dipancarkan oleh Observable menjadi Observable baru, lalu meratakan emisi dari item tersebut menjadi satu Observable." Ini barang bagus.
MBak

18

Sederhana:

[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

Ini bukan array array. Ini adalah observable (s) yang dapat diamati.

Berikut mengembalikan aliran string yang dapat diamati.

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

Sementara ini mengembalikan aliran yang dapat diamati dari aliran json yang dapat diamati

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

flatMap meratakan pengamatan secara otomatis untuk kita sehingga kita dapat mengamati aliran json secara langsung


3
Sulit untuk memahami konsep ini, dapatkah Anda menambahkan komentar ke visual yang Anda maksud "mengembalikan aliran yang dapat diamati dari aliran json yang dapat diamati". Terima kasih.
pengguna233232

@ pengguna233232, seperti [x, x, x, x] hingga [[xxx], [[xxx], [xxx]]]
serkan

Kunci untuk memahami kalimat pertama adalah memahami bahwa flatMap(dan map) tidak khusus untuk array. Operasi ini dapat didefinisikan pada container atau wrapper generik apa pun, termasuk array, kamus, "opsional", aliran reaktif, promise, pointer, dan bahkan fungsi itu sendiri. Ini adalah properti yang muncul dari konstruksi matematika yang disebut monad. Semua contoh di atas memenuhi persyaratan untuk menjadi monad, sehingga semuanya dapat diberi definisi mapdan a flatMap(dengan beberapa peringatan).
mklbtz

14

Di sini untuk menunjukkan implementasi yang setara dari flatMap menggunakan langganan.

Tanpa flatMap:

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

Dengan 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

Semoga bisa membantu.

Olivier.


13

An Observable adalah objek yang memancarkan aliran peristiwa: Berikutnya, Kesalahan, dan Selesai.

Ketika fungsi Anda mengembalikan Observable, itu tidak mengembalikan aliran, tetapi sebuah instance dari Observable. The flatMapOperator hanya memetakan contoh bahwa untuk sungai.

Itu adalah perilaku flatMapjika dibandingkan dengan map: Jalankan fungsi yang diberikan dan ratakan objek yang dihasilkan menjadi aliran.


7

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

Tanpa 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 mengubah item yang dipancarkan oleh Observable menjadi Observable, lalu meratakan emisi tersebut menjadi satu Observable

Saya tidak bodoh tetapi harus membaca ini 10 kali dan masih tidak mengerti. Ketika saya membaca cuplikan kode:

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

maka saya bisa memahami apa yang terjadi, itu melakukan dua hal:

flatMap :

  1. map : transform *) item yang dipancarkan menjadi Observable.
  2. flat : lalu gabungkan Observable tersebut menjadi satu Observable.

*) Kata transformasi mengatakan item dapat diubah menjadi sesuatu yang lain.

Kemudian operator penggabungan menjadi jelas untuk melakukan perataan tanpa pemetaan. Mengapa tidak menyebutnya mergeMap ? Tampaknya ada juga mergeMap Alias dengan nama itu untuk flatMap .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.