繁体   English   中英

使用Rx为webservice调用创建轮询请求

[英]Using Rx create a polling request for webservice call

在C#中使用Rx我正在尝试创建REST API的轮询请求。 我面临的问题是,Observable需要按顺序发送回复。 意味着如果请求A在X时间进行并且请求B在X + dx时间进行并且B的响应在A之前出现,则Observable表达式应该忽略或取消请求A.

我编写了一个示例代码,试图描述该场景。 我如何解决它只获得最新的响应,取消或忽略以前的响应。

 class Program
    {
        static int i = 0;

        static void Main(string[] args)
        {
            GenerateObservableSequence();

            Console.ReadLine();
        }

        private static void GenerateObservableSequence()
        {
            var timerData = Observable.Timer(TimeSpan.Zero,
                TimeSpan.FromSeconds(1));

            var asyncCall = Observable.FromAsync<int>(() =>
            {
                TaskCompletionSource<int> t = new TaskCompletionSource<int>();
                i++;

                int k = i;
                var rndNo = new Random().Next(3, 10);
                Task.Delay(TimeSpan.FromSeconds(rndNo)).ContinueWith(r => { t.SetResult(k); });
                return t.Task;
            });

            var obs = from t in timerData
            from data in asyncCall
            select data;

            var hot = obs.Publish();
            hot.Connect();

                hot.Subscribe(j => 
            {
                Console.WriteLine("{0}", j);
            });
        }
    }

在@Enigmativity回答之后:添加轮询Aync功能以始终采取最新响应:

 public static IObservable<T> PollingAync<T> (Func<Task<T>> AsyncCall, double TimerDuration)
        {
            return Observable
         .Create<T>(o =>
         {
             var z = 0L;
             return
                 Observable
                     .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(TimerDuration))
                     .SelectMany(nr =>
                         Observable.FromAsync<T>(AsyncCall),
                         (nr, obj) => new { nr, obj})
                     .Do(res => z = Math.Max(z, res.nr))
                     .Where(res => res.nr >= z)
                     .Select(res => res.obj)
                     .Subscribe(o);
         });

    }

这是一种常见的情况,可以简单地修复。

您的示例代码的关键部分是

var obs = from t in timerData
          from data in asyncCall
          select data;

这可以理解为“在每个值timerData得到所有值asyncCall ”。 这是SelectMany (或FlatMap )运算符。 SelectMany运算符将从内部序列( asyncCall )中获取所有值,并在收到它们时返回它们的值。 这意味着您可以获得无序值。

你想要的是当外部序列( timerData )产生一个新值时取消先前的内部序列。 为此,我们希望使用Switch运算符。

var obs = timerData.Select(_=>asyncCall)
                   .Switch();

完整代码可以清理到以下。 (删除冗余发布/连接,按键处理订阅)

class Program {static int i = 0;

    static void Main(string[] args)
    {
        using (GenerateObservableSequence().Subscribe(x => Console.WriteLine(x)))
        {
            Console.ReadLine();
        }
    }

    private static IObservable<int> GenerateObservableSequence()
    {
        var timerData = Observable.Timer(TimeSpan.Zero,
            TimeSpan.FromSeconds(1));

        var asyncCall = Observable.FromAsync<int>(() =>
        {
            TaskCompletionSource<int> t = new TaskCompletionSource<int>();
            i++;

            int k = i;
            var rndNo = new Random().Next(3, 10);
            Task.Delay(TimeSpan.FromSeconds(rndNo)).ContinueWith(r => { t.SetResult(k); });
            return t.Task;
        });

        return from t in timerData
               from data in asyncCall
               select data;
    }
}

- 编辑 -

看起来我误解了这个问题。 @Enigmativity提供了更准确的答案。 这是他的答案的清理。

//Probably should be a field?
var rnd = new Random();
var obs = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
        //.Select(n => new { n, r = ++i })
        //No need for the `i` counter. Rx does this for us with this overload of `Select`
        .Select((val, idx) => new { Value = val, Index = idx})
        .SelectMany(nr =>
            Observable.Timer(TimeSpan.FromSeconds(rnd.Next(3, 10))),
            (nr, _) => nr)
        //.Do(nr => z = Math.Max(z, nr.n))
        //.Where(nr => nr.n >= z)
        //Replace external State and Do with scan and Distinct
        .Scan(new { Value = 0L, Index = -1 }, (prev, cur) => {
            return cur.Index > prev.Index
                ? cur
                : prev;
        })
        .DistinctUntilChanged()
        .Select(nr => nr.Value)
        .Dump();

让我们从简化代码开始。

这基本上是相同的代码:

var rnd = new Random();

var i = 0;

var obs =
    from n in Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
    let r = ++i
    from t in Observable.Timer(TimeSpan.FromSeconds(rnd.Next(3, 10)))
    select r;

obs.Subscribe(Console.WriteLine);

我得到了这样的结果:

2
1
3
4
8
5
11
6
9
7
10

或者,这可以写成:

var obs =
    Observable
        .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
        .Select(n => ++i)
        .SelectMany(n =>
            Observable.Timer(TimeSpan.FromSeconds(rnd.Next(3, 10))), (n, _) => n);

所以,现在为您的要求:

如果请求A在X时间进行,并且请求B在X + dx时间进行,并且B的响应在A之前出现,则Observable表达式应该忽略或取消请求A.

这是代码:

var rnd = new Random();

var i = 0;
var z = 0L;

var obs =
    Observable
        .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
        .Select(n => new { n, r = ++i })
        .SelectMany(nr =>
            Observable.Timer(TimeSpan.FromSeconds(rnd.Next(3, 10))), (nr, _) => nr)
        .Do(nr => z = Math.Max(z, nr.n))
        .Where(nr => nr.n >= z)
        .Select(nr => nr.r);

我不喜欢使用.Do喜欢这样,但我想不出另类。

这给了这样的事情:

1
5
8
9
10
11
14
15
16
17
22

请注意,值只是升序。

现在,您真的应该使用Observable.Create来封装您正在使用的状态。 所以你的最终观察结果应如下所示:

var obs =
    Observable
        .Create<int>(o =>
        {
            var rnd = new Random();
            var i = 0;
            var z = 0L;
            return
                Observable
                    .Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
                    .Select(n => new { n, r = ++i })
                    .SelectMany(nr =>
                        Observable.Timer(TimeSpan.FromSeconds(rnd.Next(3, 10))),
                        (nr, _) => nr)
                    .Do(nr => z = Math.Max(z, nr.n))
                    .Where(nr => nr.n >= z)
                    .Select(nr => nr.r)
                    .Subscribe(o);
        });

暂无
暂无

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

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