C# 使用Rx为webservice调用创建轮询请求
在C中使用Rx,我试图创建一个对RESTAPI的轮询请求。我面临的问题是,可观察到的需要按顺序发送响应。表示如果请求A在X时间发出,请求B在X+dx时间发出,且B的响应在A之前,则可观察表达式应忽略或取消请求A 我已经编写了一个示例代码,试图描述这个场景。如何修复它以仅获取最新的响应并取消或忽略以前的响应C# 使用Rx为webservice调用创建轮询请求,c#,recursion,system.reactive,reactive-programming,polling,C#,Recursion,System.reactive,Reactive Programming,Polling,在C中使用Rx,我试图创建一个对RESTAPI的轮询请求。我面临的问题是,可观察到的需要按顺序发送响应。表示如果请求A在X时间发出,请求B在X+dx时间发出,且B的响应在A之前,则可观察表达式应忽略或取消请求A 我已经编写了一个示例代码,试图描述这个场景。如何修复它以仅获取最新的响应并取消或忽略以前的响应 class Program { static int i = 0; static void Main(string[] args)
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 answer之后:添加了轮询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生成新值时,需要取消以前的内部序列。要做到这一点,我们需要使用开关操作符
var obs = timerData.Select(_=>asyncCall)
.Switch();
完整代码可以清理到以下位置。已删除冗余发布/连接,按键时释放订阅
班级计划
{
静态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;
}
}
让我们从简化代码开始 这基本上是相同的代码:
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
那么,现在请满足您的要求:
如果请求A在X时间发出,请求B在X+dx时间发出,且B的响应在A之前,则可观察表达式应忽略或取消请求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);
我不喜欢使用。喜欢这样做,但我还想不出其他选择
这就给了这样一种东西:
1
5
8
9
10
11
14
15
16
17
22
这不总是只从最新的请求中获取值吗?我对这个问题的理解是,如果发生了两个对asyncCall的调用,并且结果按调用顺序返回,那么这两个结果都是有效的,但是如果第二个调用首先返回,那么忽略第一个调用的结果。根据您对问题的理解,然后您的假设也是正确的。@LeeCampbell关于如何处理我们已忽略的以前调用的异常的任何建议。我将调用一个异步函数,而不是Observable.TimerTimeSpan.FromSecondsrnd.Next3,如更新后的问题所示。此异步函数可能引发异常。因此,我希望取消以前的异步调用并在不结束IObservable流的情况下处理异常。一般来说,Rx是关于合成序列的,一旦序列出错,它就被视为终端。因此,从这个意义上说,您的用例不受支持。但是,在这种情况下,您可以使用Catch操作符并从中产生适当的值。看看我们如何避免出现异常从使用where子句过滤的旧响应中?@BalrajSingh-那么,如果我用一个偶尔抛出错误的操作替换++I?这是一种可能的情况吗?会发生什么样的错误?我的意思是,我将用对web ser的异步调用来替换它,而不是Observable.TimerTimeSpan.fromssecondsrnd.Next3,10反之,它可能会抛出任何类型的http错误。在这种情况下,我需要取消我发出的所有旧请求,以便它不会通过任何错误,如果它抛出,那么我应该有一种方法来处理它,而不结束我的可观察流。我已经用调用任何异步函数的示例代码更新了该问题,并仅获取最新的响应。Th如果异步函数执行,is可能会引发异常。Enigmativity,我提供了一个答案的实现,没有外部变量和Do语句。在开箱思考方面做得很好。
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);
1
5
8
9
10
11
14
15
16
17
22
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);
});