Reactive approach to simple imperative task

后端 未结 3 1297
予麋鹿
予麋鹿 2021-01-05 07:54

Application requirements:

  • subscribe to two event streams A and B
  • for each A event there should be corresponding B event some time later
  • the a
相关标签:
3条回答
  • 2021-01-05 08:06

    This should work. The types differ, I didn't want to guess your probably abstract datatypes. You can apply them pretty easily (function parameters, key comparison and Select statements)

    Idea is that for every emited value from a, we take the first value emited either by b.Where(keys match) or timeout (presented by Observable.Timer) and make our Select based on this information.

    I assumed that in timeout cases you also want on OnNext notification with some error provisioning mechanism.

    private IObservable<string> MonitorAB(IObservable<long> a, IObservable<long> b,
                                          TimeSpan threshold)
    {
        return Observable.Create<string>((obs) =>
        {
            a = a.Publish().RefCount();
            b = b.Publish().RefCount();
            return a.Subscribe(i =>
            {
                Observable.Merge(Observable.Timer(threshold).Select(_ => $"Timeout for A{i}"),
                                 b.Where(j => j == i).Select(_ => $"Got matching B for A{i}"))
                          .Take(1)
                          .Subscribe(obs.OnNext);
            });
        });
    }
    

    I tested it like this.

    private void Test()
    {
        var a = Observable.Interval(TimeSpan.FromSeconds(2)).Take(5);
        var b = Observable.Interval(TimeSpan.FromSeconds(5)).Take(5);
        MonitorAB( a, b, TimeSpan.FromSeconds(13)).Subscribe(Console.WriteLine);
    }
    

    EDIT: to test the out of order case, you can flip the B stream e.g.

    var b = Observable.Interval(TimeSpan.FromSeconds(2)).Select(i  => 4 - i).Take(5);
    
    0 讨论(0)
  • 2021-01-05 08:08

    I think this does what you want, albeit it is C# rather than Java - I'm sure you can convert easily enough.

    private IObservable<string> MonitorAB(
        IObservable<long> a, IObservable<long> b, TimeSpan threshold)
    {
        return Observable.Create<string>(o =>
            b.Publish(pb =>
                a.Select(ax =>
                    pb.Where(pbx => pbx == ax)
                        .Take(1)
                        .Timeout(threshold, Observable.Return(-1L))
                        .Select(pbx => String.Format("{0},{1}", ax, pbx)))
                .Merge())
                .Subscribe(o));
    }
    

    So this simply uses the inline .Publish to make sure b is hot within the query. The outer .Select filters the published pb observable for those that match the value from a (matching a & b), takes only one cause we only want one, and then does a .Timeout for the threshold time and returns a -1L (long) if the timeout is reached. The inner .Select just turns the two long values into a single string. At this point the query is an IObservable<IObservable<string>> so the .Merge flattens it out.

    0 讨论(0)
  • 2021-01-05 08:24

    Assumptions:

    • the B stream is hot,
    • You have a return type that can represent a Match or a Mismatch (instead of using OnError/Timeout which will terminate the subscription)

    Then this would be fine.

    AStream.SelectMany(a =>
        BStream.Where(b => b == a)
            .Select(b => new MatchMade(a, b))
            .Take(1)
            .Timeout(matchTimeout)
            .Catch<TimeoutException>(ex=>Observable.Return(new NoMatchMade(a)))
    )
    
    0 讨论(0)
提交回复
热议问题