如何使用Rx.Net实现状态轮询?

时间:2017-11-30 15:08:51

标签: c# system.reactive reactive-programming polling

我现在已经玩了两天,试图使用.Net的反应性扩展来接受反应式编程领域。

我构建了一个状态轮询的用例,假设有一个虚拟Web API和一个轮询状态对象的被动客户端。

我尝试了以下代码:

        // Creates an observable that ticks each 1 second
        var ticksObservable = Observable.Interval(TimeSpan.FromMilliseconds(1000));



        // Creates a new observable transforming each tick to a string status requested from the api
        var coldStatusPollerObservable = ticksObservable.Select(tick =>
        {
            Console.WriteLine("Sending Request");
            var tsk = client.GetStatus(1); // Http get request to a web api resource (id == 1 just for demo)
            tsk.Wait();
            return tsk.Result;

        }
        );

       // Subscribe and print results on console
       coldStatusPollerObservable.Subscribe(
        status => Console.WriteLine(status), ex => Console.WriteLine(ex.Message)
        );

一切都很好,我有预期的输出:

{"status":"waiting"}
{"status":"running"}
{"status":"running"}
{"status":"running"}
{"status":"ok"}

然后我添加了另一个约束,它是从Web API返回的随机错误请求。 发生的问题是我无法正确处理异常。 tsk.wait()中发生异常,我所期望的是它只会触发传递给Subscribe method onError 操作(ex => Console.WriteLine(ex.Message))

Q1:在这种情况下处理异常的正确方法是什么? Q2:使用Rx.NET进行轮询是否有更清晰的实现?

PS:我使用的是Rx.NET 3.1.1

1 个答案:

答案 0 :(得分:1)

如果可以,您基本上希望避免使用任何.Wait()阻止呼叫。 Rx附带了一个专门用于处理任务的运算符 - Observable.FromAsync

所以你的基本查询现在变为:

var coldStatusPollerObservable =
    from tick in ticksObservable
    from status in Observable.FromAsync(() => client.GetStatus(1))
    select status;

如果您需要控制台消息,请执行以下操作:

var coldStatusPollerObservable =
    from tick in ticksObservable
    from status in Observable.FromAsync(() =>
    {
        Console.WriteLine("Sending Request");
        return client.GetStatus(1);
    })
    select status;

请记住,尽可能坚持使用内置的操作符。

你可以处理这样的例外:

void Main()
{
    var coldStatusPollerObservable =
        from tick in Observable.Interval(TimeSpan.FromMilliseconds(1000))
        from status in
            Observable
                .FromAsync(() => client.GetStatus(1))
                .Catch<string, Exception>(ex => Observable.Return("Error"))
        select status;

    coldStatusPollerObservable.Subscribe(x => Console.WriteLine(x), ex => Console.WriteLine(ex.Message));
}

public static class client
{
    private static int _counter = 0;
    public static Task<string> GetStatus(int id)
    {
        if (_counter++ == 5)
            throw new Exception();
        return Task.Run(() => _counter.ToString());
    }
}

这给出了:

1
2
3
4
5
Error
7
8
9
10
11
12
13
14
15
...