我想将基于事件的遗留方法转换为基于观察的方法,但我对Rx很新,所以我现在陷入困境。
我有一个事件源,现在可以观察到。在某个时间点,我必须启动一个方法,该方法通过返回行中的下一个元素结束,如果超时则返回null。
基于事件的方法如下所示:
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
ReaderEvent result = null;
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(new [] { topLevelToken }))
{
cts.CancelAfter(waitFor);
EventHandler<ReaderEvent> localHandler = (o, e) =>
{
if (e.PlaceId == PlaceId)
{
result = e;
cts.Cancel();
}
};
ReaderEventHandler += localHandler;
try
{
await Task.Delay(waitFor, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
//...
}
ReaderEventHandler -= localHandler;
}
return result;
}
正如您所看到的,我们的想法是,延迟会在我等待的事件到达时取消,或者在特定时间后由配置取消令牌源。很干净。
现在,Rx版本:
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
ReaderEvent result = null;
var observable = _OnReaderEvent.FirstAsync(r => r.PlaceId == PlaceId);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(new [] { topLevelToken }))
{
cts.CancelAfter(waitFor);
using (observable.Subscribe(x => {
result = x;
cts.Cancel();
{
try
{
await Task.Delay(waitFor, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
}
}
return result;
}
不太干净......更糟糕的是...... 我也尝试过Timeout扩展。但由于这是一次性订阅,我仍然需要在处理订阅之前以某种方式等待。唯一的区别是OnError会取消本地令牌,而不是CancelAfter的内置机制。
是否有任何击球手/更简洁(更依赖Rx)的方式来做到这一点?
谢谢!
答案 0 :(得分:3)
你可以试试:
GST-17/18/SO-001311
答案 1 :(得分:1)
为什么不使用简单的Rx版代码:
public async Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return await
_OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.Buffer(waitFor, 1)
.Select(xs => xs.FirstOrDefault())
.FirstOrDefaultAsync()
.ToTask();
}
答案 2 :(得分:1)
这个问题可以用很多不同的方式解决。这是一个,使用 Amb
、Return
、Delay
和 FirstAsync
运算符:
public Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return _OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.Amb(Observable.Return(default(ReaderEvent)).Delay(waitFor))
.FirstAsync()
.ToTask();
}
在异常情况下,_OnReaderEvent
observable 在等待期间完成或完成,产生的 Task
将转换为错误状态,异常为 InvalidOperationException
"Sequence contains no元素”。
另一个实现,在功能上等同于前一个,使用 Timeout
运算符:
public Task<ReaderEvent> WaitForReaderAsync(int PlaceId, TimeSpan waitFor)
{
return _OnReaderEvent
.Where(r => r.PlaceId == PlaceId)
.FirstAsync()
.Timeout(waitFor, Observable.Return(default(ReaderEvent)))
.ToTask();
}