为什么我们需要使用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存储库中运行更复杂的示例,并在flatmap和map运算符之前和之后添加“ .do”运算符,然后仅使用Chrome调试器设置断点的原因。您会立即看到它们各自产生不同的输出
HipsterZipster

5
我认为,如果flatMap将其命名为mapThenFlatten,则不会造成混淆。
山羊

Answers:


73

当我开始看时,Rxjs我也偶然发现了那块石头。帮助我的是以下几点:

  • 来自reactx.io的文档。例如,对于flatMaphttp : //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将type函数T' -> Observable<T>作为其参数,然后返回Observable<T>map接受type函数T' -> T并返回Observable<T>

    回到您的示例,您有一个从URL字符串生成promise的函数。所以T' : stringT : promise。而从我们之前说的promise : Observable<T''>,所以T : Observable<T''>,有T'' : html。如果将那个Promise Production函数放入中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 '18

@JacobStamm我同意。只是使事情变得容易。
CruelEngine '19

这个语法是什么意思T’ -> T?我将其理解T为通用名称,但是撇号和非肥胖箭头是什么?
1252748

您可以用X或Y代替T',而无需更改答案中的任何含义。箭头是用于类型签名的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]]

当您有一个Observable的结果是更多Observable时,可以使用flatMap。

如果您有一个由另一个可观察对象产生的可观察对象,则您不能直接过滤,缩小或映射它,因为您有一个可观察对象而不是数据。如果产生一个可观察的图像,则在地图上选择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 将一个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

31

人们倾向于通过给出如下定义来使事情变得过于复杂

flatMap将Observable发出的项目转换为Observable,然后将这些项目的排放展平为单个Observable

我发誓这个定义仍然让我感到困惑,但是我将以最简单的方式(通过使用示例)来解释它

我们的情况:我们有一个Observable,它返回将用于进行HTTP调用的数据(简单URL),该HTTP调用将返回一个包含我们所需数据的Observable,因此您可以形象地看到这种情况:

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

@ user233232,例如[x,x,x,x]到[[xxx],[[xxx],[xxx]]]
serkan,

理解第一句话的关键是要了解flatMap(和map)对数组不是特殊的。可以在任何通用容器或包装器上定义这些操作,包括数组,字典,“可选”,响应流,promise,指针,甚至函数本身。这是一种称为monad的数学构造的新兴属性。上面所有示例都满足成为monad的要求,因此可以给它们一个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时,它不是在返回流,而是一个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

我并不傻,但是不得不读了十遍却仍然听不懂。当我阅读代码片段时:

[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. flat:然后将那些可观察对象合并为一个可观察对象。

*)转换词表示可以将项目转换成其他形式。

然后,合并运算符变得很清楚,无需进行映射即可进行展平。为什么不将其称为mergeMap?似乎还有一个Alias mergeMap,其名称为flatMap

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.