map vs concatMap

时间:2018-01-27 01:56:53

标签: javascript angular rxjs

我无法找到这个问题的答案,但concat mapmap之间有什么区别?具体来说,我有一个让我感到困惑的例子:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .concatMap((authDomains) => authDomains.map((domain) => this.toDomain(domain, connectionsObservable)))
    .filter((authDomain) => this.isValidDomain(authDomain))
    .toArray();

这是从服务getAuthDomains()调用一个方法,它返回了一个包含3个域的数组。当我使用上面的实现时,我最终得到了一个由4个数组组成的数组,每个数组都有一个包含3个域的数组。不知何故,它使响应中的数据量翻了两番。

然后我切换到以下实现:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .map(authDomains => authDomains.map(domain => this.toDomain(domain, connectionsObservable)))
    .map(authDomains => authDomains.filter(domain => this.isValidDomain(domain)));  

这正是我所需要的 - 一个被正确过滤的数组。

那么为什么切换到map会大大改变结果呢?我的操作顺序是否重要?

由于

1 个答案:

答案 0 :(得分:6)

简单地说两位运营商做了以下事情:

  • map() - 将每个值投射到一个不同的值中,该值在链中进一步传播:

    .map(v => v * 2)
    

    这与Array.map()基本相同。

  • concatMap() - 将每个值投影到Observable中并订阅它。运算符总是只订阅一个内部Observable,因此如果源Observable更快地发出值,它们将被缓存在concatMap()内:

    .concatMap(v => Observable.of(v * 2))
    

    ...或者通常在Angular中你会做这样的事情:

    .concatMap(v => this.http.get(`whatever/${v}`))
    

但是有趣的部分来了。在RxJS 5中,订阅内部Observables(从投影函数返回的Observable等)的运算符实际上期望所谓的“可观察输入”。这意味着您可以自由地交换Observables,Promises,Observables类对象,以及JavaScript数组。

在内部使用subscribeToResult功能实现。请注意,如果您将数组传递给它,它将迭代它并单独发出每个值https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/subscribeToResult.ts#L17

这意味着您可以使用concatMap()将数组“解包”成单个发射。例如:

Observable.of(42)
  .concatMap(v => [1, 2, 3])
  .subscribe(console.log)

这将打印数字:

1
2
3

另一方面,如果你只使用map,它只会进一步传递数组:

Observable.of(42)
  .map(v => [1, 2, 3])
  .subscribe(console.log)

这将打印数字:

[1,2,3]

所以concatMap有时仅用于解析来自源Observable的数组concatMap(array => array),这正是代码中正在发生的事情。

哪一个更好取决于你。通常,在Array对象本身上执行简单操作(如mapfilter)更容易,而不是出于性能原因在Rx链中进行解包和处理。