How to join multiple IObservable sequences?

后端 未结 4 1471
無奈伤痛
無奈伤痛 2021-01-02 12:26
        var a = Observable.Range(0, 10);
        var b = Observable.Range(5, 10);
        var zip = a.Zip(b, (x, y) => x + \"-\" + y);
        zip.Subscribe(Conso         


        
4条回答
  •  挽巷
    挽巷 (楼主)
    2021-01-02 12:27

    I honestly can't think of a solution based on existing operators that works for hot sources of unknown order (that is, xs before ys vs ys before xs). Your solution seems fine (hey, if it works), but I'd make a few changes if it were my code:

    • Support cancellation properly using MutableDisposable and CompositeDisposable
    • Call OnError for exceptions thrown from the selector (making it more consistant with other operators)
    • Consider supporting completion if it's possible for one source to complete before the other

    The code below has been tested with your dual-Range input, the same inputs flipped, as well as with Empty + Never:

    public static IObservable MergeJoin(
        IObservable left, IObservable right, Func selector)
    {
        return Observable.CreateWithDisposable(o =>
        {
            Queue a = new Queue();
            Queue b = new Queue();
            object gate = new object();
    
            bool leftComplete = false;
            bool rightComplete = false;
    
            MutableDisposable leftSubscription = new MutableDisposable();
            MutableDisposable rightSubscription = new MutableDisposable();
    
            Action tryDequeue = () =>
            {
                lock (gate)
                {
                    while (a.Count != 0 && b.Count != 0)
                    {
                        if (a.Peek() == b.Peek())
                        {
                            string value = null;
    
                            try
                            {
                                value = selector(a.Dequeue(), b.Dequeue());
                            }
                            catch (Exception ex)
                            {
                                o.OnError(ex);
                                return;
                            }
    
                            o.OnNext(value);
                        }
                        else if (a.Peek() < b.Peek())
                        {
                            a.Dequeue();
                        }
                        else
                        {
                            b.Dequeue();
                        }
                    }
                }
            };
    
            leftSubscription.Disposable = left.Subscribe(x =>
            {
                lock (gate)
                {
                    if (a.Count == 0 || a.Peek() < x)
                        a.Enqueue(x);
    
                    tryDequeue();
    
                    if (rightComplete && b.Count == 0)
                    {
                        o.OnCompleted();
                    }
                }
            }, () =>
            {
                leftComplete = true;
    
                if (a.Count == 0 || rightComplete)
                {
                    o.OnCompleted();
                }
            });
    
            rightSubscription.Disposable = right.Subscribe(x =>
            {
                lock (gate)
                {
                    if (b.Count == 0 || b.Peek() < x)
                        b.Enqueue(x);
    
                    tryDequeue();
    
                    if (rightComplete && b.Count == 0)
                    {
                        o.OnCompleted();
                    }
                }
            }, () =>
            {
                rightComplete = true;
    
                if (b.Count == 0 || leftComplete)
                {
                    o.OnCompleted();
                }
            });
    
            return new CompositeDisposable(leftSubscription, rightSubscription);
        });
    }
    

提交回复
热议问题