Tại sao chúng ta cần sử dụng flatMap?


92

Tôi đang bắt đầu sử dụng RxJS và tôi không hiểu tại sao trong ví dụ này chúng ta cần sử dụng một hàm như flatMaphoặcconcatAll ; mảng của mảng ở đây là ở đâu?

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

Nếu ai đó có thể giải thích trực quan những gì đang xảy ra, điều đó sẽ rất hữu ích.


1
câu trả lời này là tuyệt vời vì các tài liệu tham khảo có giá trị mà nó cung cấp, nhưng thuật ngữ rxjs không dịch tốt sang tiếng Anh. (hình ảnh tốt hơn). Đó là lý do tại sao tôi khuyên bạn nên chạy các ví dụ đơn giản như thế này hoặc các ví dụ phức tạp hơn trong repo rxjs và thêm toán tử ".do" trước và sau toán tử bản đồ và bản đồ phẳng, sau đó chỉ cần thiết lập điểm ngắt bằng trình gỡ lỗi Chrome. bạn sẽ thấy ngay lập tức rằng mỗi sản xuất ra một sản lượng khác nhau
HipsterZipster

5
Tôi nghĩ nếu flatMapcó thể được đặt tên mapThenFlatten, thì nó sẽ ít khó hiểu hơn.

Câu trả lời:


73

Khi tôi bắt đầu có cái nhìn, Rxjstôi cũng vấp phải viên đá đó. Điều đã giúp tôi như sau:

  • tài liệu từ Reativex.io. Ví dụ flatMap: đối với : http://reactivex.io/documentation/operators/flatmap.html
  • tài liệu từ rxmarbles: http://rxmarbles.com/ . Bạn sẽ không tìm thấy flatMapở đó, mergeMapthay vào đó bạn phải nhìn vào (tên khác).
  • phần giới thiệu về Rx mà bạn đã bỏ sót: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Nó giải quyết một ví dụ rất giống nhau. Đặc biệt, nó giải quyết thực tế là một lời hứa giống như một lời hứa chỉ phát ra một giá trị có thể quan sát được.
  • cuối cùng xem thông tin loại từ RxJava. Javascript không được gõ không giúp ích gì ở đây. Về cơ bản, nếu Observable<T>biểu thị một đối tượng có thể quan sát được đẩy các giá trị kiểu T, sau đó flatMaplấy một hàm kiểu T' -> Observable<T>làm đối số của nó và trả về Observable<T>. mapnhận một hàm kiểu T' -> Tvà trả vềObservable<T> .

    Quay lại ví dụ của bạn, bạn có một hàm tạo ra các lời hứa từ một chuỗi url. Vì vậy T' : string, và T : promise. Và từ những gì chúng tôi đã nói trước đây promise : Observable<T''>, vì vậy T : Observable<T''>, với T'' : html. Nếu bạn đặt chức năng tạo ra lời hứa đó map, bạn sẽ nhận được Observable<Observable<T''>>khi những gì bạn muốn là Observable<T''>: bạn muốn người có thể quan sát phát ra các htmlgiá trị. flatMapđược gọi như vậy vì nó làm phẳng (loại bỏ một lớp có thể quan sát được) kết quả từ đó map. Tùy thuộc vào nền tảng của bạn, đây có thể là tiếng Trung Quốc đối với bạn, nhưng mọi thứ trở nên rõ ràng với tôi với thông tin nhập và bản vẽ từ đây: http://reactivex.io/documentation/operators/flatmap.html .


2
Tôi quên đề cập rằng bạn sẽ có thể đơn giản hóa return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));đến return jQuery.getJSON(requestUrl);như flatMapcũng chấp nhận một chức năng chọn mà trả về một chức năng hứa tức là loại T' -> Promise.
user3743222

2
Chà, GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) thật tuyệt vời. Tôi giới thiệu nó cho bất kỳ ai làm việc với bất kỳ thư viện ReactiveX nào như RxJS.
Jacob Stamm,

@JacobStamm tôi đồng ý. Chỉ làm cho mọi thứ dễ dàng hơn.
CruelEngine

Cú pháp này có nghĩa là T’ -> Tgì:? Tôi hiểu Tcái chung chung, nhưng dấu huyền và mũi tên không béo là gì?
1252748 13/12/19

Bạn có thể thay thế T 'bằng X hoặc Y mà không làm thay đổi nghĩa ở bất kỳ đâu trong câu trả lời. Mũi tên là ký hiệu Haskell cho chữ ký kiểu. Vì vậy, T '-> T là chữ ký cho một chức năng mà phải mất một phần tử kiểu T' và trả về một phần tử kiểu 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]]

Bạn sử dụng flatMap khi bạn có một Observable có kết quả nhiều hơn Observable.

Nếu bạn có một vật có thể quan sát được tạo ra bởi một vật thể quan sát khác, bạn không thể lọc, giảm hoặc lập bản đồ trực tiếp vì bạn có Dữ liệu quan sát không phải là dữ liệu. Nếu bạn tạo ra một bản đồ phẳng có thể quan sát được, hãy chọn bản đồ; sau đó bạn không sao.

Như trong đoạn mã thứ hai, nếu bạn đang thực hiện thao tác không đồng bộ, bạn cần sử dụng 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 chuyển đổi các mục được phát ra bởi một Có thể quan sát thành các Có thể quan sát mới, sau đó làm phẳng các phát xạ từ các mục đó thành một Có thể quan sát duy nhất.

Kiểm tra trường hợp bên dưới get("posts")trả về một Observable được "san phẳng" 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
Hay, câu trả lời đơn giản. Tôi nghĩ điều này có thể là tốt nhất.
vaughan

"flatMap biến đổi các mục được phát ra bởi một Có thể quan sát thành các Có thể quan sát mới, sau đó làm phẳng các phát xạ từ các mục đó thành một Có thể quan sát duy nhất." Đây là công cụ tuyệt vời.
MBak

31

Mọi người có xu hướng phức tạp hóa mọi thứ quá mức bằng cách đưa ra định nghĩa:

flatMap chuyển đổi các mục được phát ra bởi một Có thể quan sát thành Có thể quan sát, sau đó làm phẳng các phát xạ từ các mục đó thành một Có thể quan sát duy nhất

Tôi thề rằng định nghĩa này vẫn khiến tôi bối rối nhưng tôi sẽ giải thích nó theo cách đơn giản nhất đó là sử dụng một ví dụ

Tình huống của chúng tôi : chúng tôi có một tệp có thể quan sát trả về dữ liệu (URL đơn giản) mà chúng tôi sẽ sử dụng để thực hiện lệnh gọi HTTP sẽ trả về một tệp có thể quan sát chứa dữ liệu mà chúng tôi cần để bạn có thể hình dung tình huống như sau:

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

để bạn có thể thấy, chúng tôi không thể tiếp cận trực tiếp dữ liệu chúng tôi cần, vì vậy, cách đầu tiên để truy xuất dữ liệu, chúng tôi có thể sử dụng chỉ các đăng ký bình thường như sau:

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

điều này hoạt động nhưng như bạn có thể thấy, chúng tôi phải lồng các đăng ký để lấy dữ liệu của chúng tôi, điều này hiện tại trông không tệ nhưng hãy tưởng tượng chúng ta có 10 đăng ký lồng nhau sẽ trở nên không thể xác định được.

vì vậy, cách tốt hơn để xử lý điều này là chỉ sử dụng nhà điều hành flatMapsẽ làm điều tương tự nhưng khiến chúng tôi tránh đăng ký lồng nhau đó:

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

19

Đơn giản:

[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

Nó không phải là một mảng của mảng. Đó là một (các) quan sát được.

Phần sau trả về một dòng chuỗi có thể quan sát được.

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

Trong khi điều này trả về một luồng json có thể quan sát được

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

flatMap tự động làm phẳng dòng có thể quan sát cho chúng tôi để chúng tôi có thể quan sát trực tiếp dòng json


3
Khó hiểu khái niệm này, bạn có thể vui lòng thêm comment để hình dung ý mình là "trả về một luồng json có thể quan sát được". cảm ơn.
user233232

@ user233232, như [x, x, x, x] thành [[xxx], [[xxx], [xxx]]]
serkan

Chìa khóa để hiểu câu đầu tiên là hiểu rằng flatMap(và map) không đặc biệt đối với mảng. Có thể xác định các hoạt động này trên bất kỳ vùng chứa hoặc trình bao bọc chung nào, bao gồm mảng, từ điển, "tùy chọn", luồng phản ứng, hứa hẹn, con trỏ và thậm chí chính các hàm. Đây là một thuộc tính nổi lên của một cấu trúc toán học được gọi là đơn nguyên. Tất cả các ví dụ trên đều đáp ứng các yêu cầu để trở thành đơn nguyên, và vì vậy tất cả chúng đều có thể được đưa ra định nghĩa mapvà một flatMap(với một số lưu ý).
mklbtz 20/09/2018

14

Ở đây để hiển thị cách triển khai tương đương của flatMap bằng cách sử dụng đăng ký.

Không có bản đồ phẳng:

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

Với 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

Hy vọng nó có thể giúp.

Olivier.


13

Một đối tượng có thể quan sát là một đối tượng phát ra một luồng sự kiện: Tiếp theo, Lỗi và Đã hoàn thành.

Khi hàm của bạn trả về một Observable, nó không trả về một luồng mà là một thể hiện của Observable. Các flatMapnhà điều hành chỉ đơn giản là đồ dụ đó để một dòng suối.

Đó là hành vi của flatMapkhi so sánh với map: Thực thi chức năng đã cho và làm phẳng đối tượng kết quả thành một dòng.


7

Với bản đồ phẳng

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

Không có bản đồ phẳng

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 chuyển đổi các mục được phát ra bởi một Có thể quan sát thành Có thể quan sát, sau đó làm phẳng các phát xạ từ các mục đó thành một Có thể quan sát duy nhất

Tôi không ngốc nhưng đã phải đọc cái này 10 lần mà vẫn không hiểu. Khi tôi đọc đoạn mã:

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

sau đó tôi có thể hiểu chuyện gì đang xảy ra, nó thực hiện hai điều:

phẳngMap :

  1. map : biến đổi *) các mục được phát ra thành các mục có thể quan sát được.
  2. phẳng : sau đó hợp nhất các Có thể quan sát đó thành một Có thể quan sát.

*) Từ biến hình cho biết vật phẩm có thể được biến đổi thành vật khác.

Sau đó, toán tử hợp nhất trở nên rõ ràng, nó thực hiện việc làm phẳng mà không cần ánh xạ. Tại sao không gọi nó là mergeMap ? Có vẻ như cũng có một Alias mergeMap với tên đó cho flatMap .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.