RxJS MergeMap如何工作?

时间:2017-02-08 18:17:04

标签: javascript angular rxjs observable

我根本不理解mergeMap的目的。我听过两个“解释:

  1. “它就像在LINQ中的SelectAll” - nope。
  2. “嗯,这是RxJS mergemap的组合” - nope(或者我无法复制)。
  3. 请考虑以下代码:

        var obs1 = new Rx.Observable.interval(1000);
        var obs2 = new Rx.Observable.interval(1000);
    
        //Just a merge and a map, works fine
        obs1.merge(obs2).map(x=> x+'a').subscribe(
          next => console.log(next)
        )
    
        //Who know what - seems to do the same thing as a plain map on 1 observable
        obs1.mergeMap(val => Rx.Observable.of(val + `B`))
            .subscribe(
              next => console.log(next)
            )
    

    JS Bin

    标有“谁知道什么”的最后一篇文章仅仅是obs1上的地图 - 有什么意义?

    mergeMap实际上做了什么?什么是有效用例的示例? (最好带一些代码)

    根本没有帮助我的文章(上面的mergeMap代码来自其中一个):12

2 个答案:

答案 0 :(得分:100)

tl; dr; mergeMapmap更强大。了解mergeMap是获取Rx全部功能的必要条件。

的相似性

  • mergeMapmap对单个流有效(对zipcombineLatest

  • mergeMapmap都可以转换流的元素(与filterdelay

差异

地图

  • 无法更改源流的大小(假设:map本身不会throw);对于源中的每个元素,只发出一个mapped元素; map不能忽略元素(例如filter);

  • 在默认调度程序的情况下,转换同步进行; 100%清除:源流可以异步传递其元素,但每个下一个元素立即mapped并进一步重新发射; map无法及时更改元素,例如delay

  • 对返回值没有限制

  • idx => x

mergeMap

  • 可以改变源流的大小;对于每个元素,可能存在创建/发出的任意数量(0,1或多个)新元素

  • 它提供了对异步性的完全控制 - 既可以创建/发出新元素,也可以同时处理源流中有多少元素;例如,假设源流发出10个元素,但maxConcurrency设置为2,那么将立即处理两个第一个元素,其余8个缓冲;一旦处理完{1}}之一,源流中的下一个元素将被处理,依此类推 - 这有点棘手,但请看下面的例子

  • 所有其他运营商只能使用completemergeMap构造函数

  • 来实施
  • 可用于递归异步操作

  • 返回值必须是Observable类型(或者Rx必须知道如何从中创建observable - 例如promise,array)

  • Observableid

数组类比

x => Rx.Observable.of(x)

这个类比并没有显示完整的图片,它基本上对应let array = [1,2,3] fn map mergeMap x => x*x [1,4,9] error /*expects array as return value*/ x => [x,x*x] [[1,1],[2,4],[3,9]] [1,1,2,4,3,9] .mergeMap设置为1.在这种情况下,元素将按上面的顺序排序,但一般情况下它不需要是这样。我们唯一的保证是新元素的排放将按其在基础流中的位置排序。例如:maxConcurrency[3,1,2,4,9,1]有效,但[2,3,1,1,9,4]不是(因为[1,1,4,2,3,9]是在基础流中4之后发出的)。

使用2的几个例子:



mergeMap

// implement .map with .mergeMap
Rx.Observable.prototype.mapWithMergeMap = function(mapFn) {
  return this.mergeMap(x => Rx.Observable.of(mapFn(x)));
}

Rx.Observable.range(1, 3)
  .mapWithMergeMap(x => x * x)
  .subscribe(x => console.log('mapWithMergeMap', x))

// implement .filter with .mergeMap
Rx.Observable.prototype.filterWithMergeMap = function(filterFn) {
  return this.mergeMap(x =>
    filterFn(x) ?
    Rx.Observable.of(x) :
    Rx.Observable.empty()); // return no element
}

Rx.Observable.range(1, 3)
  .filterWithMergeMap(x => x === 3)
  .subscribe(x => console.log('filterWithMergeMap', x))

// implement .delay with .mergeMap 
Rx.Observable.prototype.delayWithMergeMap = function(delayMs) {
  return this.mergeMap(x =>
    Rx.Observable.create(obs => {
      // setTimeout is naive - one should use scheduler instead
      const token = setTimeout(() => {
        obs.next(x);
        obs.complete();
      }, delayMs)
      return () => clearTimeout(token);
    }))
}

Rx.Observable.range(1, 3)
  .delayWithMergeMap(500)
  .take(2)
  .subscribe(x => console.log('delayWithMergeMap', x))

// recursive count
const count = (from, to, interval) => {
  if (from > to) return Rx.Observable.empty();
  return Rx.Observable.timer(interval)
    .mergeMap(() =>
      count(from + 1, to, interval)
      .startWith(from))
}

count(1, 3, 1000).subscribe(x => console.log('count', x))

// just an example of bit different implementation with no returns
const countMoreRxWay = (from, to, interval) =>
  Rx.Observable.if(
    () => from > to,
    Rx.Observable.empty(),
    Rx.Observable.timer(interval)
    .mergeMap(() => countMoreRxWay(from + 1, to, interval)
      .startWith(from)))

const maxConcurrencyExample = () =>
  Rx.Observable.range(1,7)
    .do(x => console.log('emitted', x))
    .mergeMap(x => Rx.Observable.timer(1000).mapTo(x), 2)
    .do(x => console.log('processed', x))
    .subscribe()

setTimeout(maxConcurrencyExample, 3100)




答案 1 :(得分:19)

.mergeMap()可让您将更高阶的Observable压缩为单个流。例如:



Rx.Observable.from([1,2,3,4])
  .map(i => getFreshApiData())
  .subscribe(val => console.log('regular map result: ' + val));

//vs

Rx.Observable.from([1,2,3,4])
  .mergeMap(i => getFreshApiData())
  .subscribe(val => console.log('mergeMap result: ' + val));

function getFreshApiData() {
  return Rx.Observable.of('retrieved new data')
    .delay(1000);
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.0/Rx.js"></script>
&#13;
&#13;
&#13;

有关.xxxMap()运算符的详细解释,请参阅我在这个问题的答案:Rxjs - How can I extract multiple values inside an array and feed them back to the observable stream synchronously