简体   繁体   English

如何实现RxJS flatMapLatestTwo

[英]How to implement RxJS flatMapLatestTwo

RxJS's flatMapLatest flattens the latest (only one) nested Observable. RxJS的flatMapLatest使最新的(只有一个)嵌套的Observable变平。 I have a use case where I don't want flatMap (that flattens all nested Observables from the past), and I don't want flatMapWithConcurrency (because it favors old Observables, not latest Observables), so what I want is a flatMapLatestTwo or some version of flatMapLatest where you can specify the maximum number of concurrent nested Observables, eg flatMapLatest(2, selectorFn) . 我有一个用例,我不想要flatMap(从过去扁平化所有嵌套的Observable),我不想要flatMapWithConcurrency(因为它有利于旧的Observables,而不是最新的Observables),所以我想要的是flatMapLatestTwo或者某个版本的flatMapLatest,您可以在其中指定并发嵌套Observable的最大数量,例如flatMapLatest(2, selectorFn)

Here is my desired output ( _X refers to nested Observable X and eX refers to its X-th onNext event): 这是我想要的输出( _X表示嵌套的Observable XeX表示其第X个onNext事件):

_0e0
_0e1
 _1e0
_0e2
 _1e1
  _2e0
 _1e2
  _2e1
   _3e0
  _2e2
   _3e1
    _4e0
   _3e2
    _4e1
   _3e3
    _4e2
    _4e3

This is what flatMapLatest produces: 这就是flatMapLatest产生的:

_0e0
_0e1
 _1e0
 _1e1
  _2e0
  _2e1
   _3e0
   _3e1
    _4e0
    _4e1
    _4e2
    _4e3

I'd prefer a solution which uses existing operators instead of implementing this low-level. 我更喜欢使用现有运营商而不是实现这种低级别的解决方案。

This looks naive. 这看起来很幼稚。 I'm looking for ways to improve, but here it is: 我正在寻找改进方法,但这里是:

Rx.Observable.prototype.flatMapLatestN = function (count, transform) {

  let queue = [];

  return this.flatMap(x => {
    return Rx.Observable.create(observer => {

      let disposable;

      if (queue.length < count) {
        disposable = transform(x).subscribe(observer);
        queue.push(observer);
      }
      else {
        let earliestObserver = queue[0];
        if (earliestObserver) {
          earliestObserver.onCompleted();
        }

        disposable = transform(x).subscribe(observer);
        queue.push(observer);
      }

      return () => {
        disposable.dispose();
        let i = queue.indexOf(observer);
        queue.splice(i, 1);
      };
    });
  });
};

To test out: 测试:

function space(n) {
  return Array(n+1).join(' ');
}

Rx.Observable
  .interval(1000)
  .take(6)
  .flatMapLatestN(2, (x) => {
    return Rx.Observable
      .interval(300)
      .take(10)
      .map(n => `${space(x*4)}${x}-${n}`);
  })
  .subscribe(console.log.bind(console));

It will output: 它将输出:

0-1
0-2
0-3
    1-0
0-4
    1-1
0-5
    1-2
    1-3
        2-0
    1-4
        2-1
    1-5
        2-2
        2-3
            3-0
        2-4
            3-1
        2-5
            3-2
            3-3
                4-0
            3-4
                4-1
            3-5
                4-2
                4-3
                    5-0
                4-4
                    5-1
                4-5
                    5-2
                4-6
                    5-3
                4-7
                    5-4
                4-8
                    5-5
                4-9
                    5-6
                    5-7
                    5-8
                    5-9

Here's a solution that uses the built in operators. 这是一个使用内置运算符的解决方案。 First we split the Observable into N observables where each observable has the corresponding Nth latest item in the sequence. 首先,我们将Observable分割为N个可观察量,其中每个可观察量在序列中具有相应的第N个最新项目。 Then we flatMapLatest each of these and merge them. 然后我们flatMapLatest每一个并合并它们。

Rx.Observable.prototype.flatMapLatestN = function(N, selector, thisArg) {
    var self = this;
    return Rx.Observable.merge(Rx.Observable.range(0, N).flatMap(function(n) {
        return self.filter(function(x, i) {
            return i % N === n;
        }).flatMapLatest(selector, thisArg);
    }));
}

Or in ES2015: 或者在ES2015中:

Rx.Observable.prototype.flatMapLatestN = function(N, selector, thisArg) {
    const {merge, range} = Rx.Observable;
    return merge(
        range(0, N)
            .flatMap(n => 
                this.filter((x, i) => i % N === n).flatMapLatest(selector, thisArg))
    );
}

Using the same test as Daiwei: 使用与戴伟相同的测试:

Output for N=1 (same as flatMapLatest ): 输出N=1 (与flatMapLatest相同):

0-0
0-1
0-2
    1-0
    1-1
    1-2
        2-0
        2-1
        2-2
            3-0
            3-1
            3-2
                4-0
                4-1
                4-2
                    5-0
                    5-1
                    5-2
                    5-3
                    5-4
                    5-5
                    5-6
                    5-7
                    5-8
                    5-9

Output for N=2 : N=2输出:

0-0
0-1
0-2
0-3
    1-0
0-4
    1-1
0-5
    1-2
    1-3
        2-0
    1-4
        2-1
    1-5
        2-2
        2-3
            3-0
        2-4
            3-1
        2-5
            3-2
            3-3
                4-0
            3-4
                4-1
            3-5
                4-2
                4-3
                    5-0
                4-4
                    5-1
                4-5
                    5-2
                4-6
                    5-3
                4-7
                    5-4
                4-8
                    5-5
                4-9
                    5-6
                    5-7
                    5-8
                    5-9

Output for N=3 : N=3输出:

0-0
0-1
0-2
0-3
    1-0
0-4
    1-1
0-5
    1-2
0-6
    1-3
        2-0
0-7
    1-4
        2-1
0-8
    1-5
        2-2
    1-6
        2-3
            3-0
    1-7
        2-4
            3-1
    1-8
        2-5
            3-2
        2-6
            3-3
                4-0
        2-7
            3-4
                4-1
        2-8
            3-5
                4-2
            3-6
                4-3
                    5-0
            3-7
                4-4
                    5-1
            3-8
                4-5
                    5-2
            3-9
                4-6
                    5-3
                4-7
                    5-4
                4-8
                    5-5
                4-9
                    5-6
                    5-7
                    5-8
                    5-9

My answer is in C#. 我的答案是在C#中。 Sorry. 抱歉。

You didnt specify if your observables were hot or cold. 你没有说明你的观察结果是热还是冷。 It could be that your weird numbers came from the fact that your windowing made a new subscription to your 'inner' observable as it was pushed down from 1st in the window to 2nd. 可能是你的奇怪数字来自这样一个事实:你的窗口对你的'内部'观察者进行了新的订阅,因为它从窗口的第一个被推到了第二个。 My first attempt did the same: 我的第一次尝试做了同样的事:

var q = Observable.Interval(TimeSpan.FromSeconds(1))
        .Select(i => Observable.Interval(TimeSpan.FromMilliseconds(100))
        .Select(x => $"_{i}e{x}"));

w = q.Zip(q.Skip(1), (prev, curr)=> prev.Merge(curr)).Switch(); 

I think you will struggle to do anything to avoid this that isnt dog shit ugly unless you create an operator as there is state involved in managing this. 我认为你很难做任何事情来避免这种情况,除非你创建一个运营商,因为有一个状态参与管理这个问题。 (Obviously someone will prove me wrong here!!) (显然有人会在这里证明我的错!)

Here was my operator approach which also happens to support the parameterization you asked for. 这是我的操作员方法,它也恰好支持您要求的参数化。

public static class Ex
{
    public static IObservable<T> SelectManyLatest<T>(this IObservable<IObservable<T>> source, int latest)
    {
        return Observable.Create<T>(o => 
        {
            var d = new Queue<IDisposable>();

            source.Subscribe(os => 
            {
                if(d.Count == latest)
                    d.Dequeue().Dispose();

                d.Enqueue(os.Subscribe(o.OnNext, o.OnError, () => {}));

            }, o.OnError, o.OnCompleted);

            return Disposable.Create(()=>new CompositeDisposable(d).Dispose());
        });     
    }
}

Again, sorry for the C# 再次,抱歉C#

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM