System.Reactive基础——超时
问题
假设你希望事件能够在一定的时间内到达,并需要确保即便事件未能到达,程序也会及时响应。在大多数情况下,这类事件是单个异步操作,比如从Web服务请求获取响应。
解决方案
Timeout运算符会在其输入流上创建滑动的超时窗口。每当有新事件到来时,该超时窗口就会充值,如果超时过期,却未在窗口中发现事件,Timeout运算符就会包含TimeoutException的OnError通知终止该流
为了启动Web请求,代码使用了ToObservable将Task<T>转换为IObservable<T>
void GetWitTimeout(HttpClient client)
{
client.GetStringAsync("http://exampleurl").ToObservable().Timeout(TimeSpan.FromSeconds(1)).Subscribe(
x => Trace.WriteLine($"{DateTime.Now.Second};Saw {x.Length}"), ex => Trace.WriteLine(ex));
}
C#
Timeout还可以将它用在任何的事件流上
Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler => MouseMove -= handler).Select(x => x.EventArgs.Location).Timeout(TimeSpan.FromSeconds(1)).Subscribe(x => Trace.WriteLine($"{DateTime.Now.Second}:Saw{x.X + x.Y}"), ex => Trace.WriteLine(ex));
C#
以我的计算机为例,鼠标先移动一会,然后静止1秒,结果如下:
51:Saw634
51:Saw631
“WinFormsApp6.exe”(CoreCLR: clrhost): 已加载“C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.17\System.Threading.ThreadPool.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
System.TimeoutException: The operation has timed out.
C#
注意,一旦TimeoutException发送至OnError,流就结束了,再也不会录入任何鼠标移动事件,也许这并非你期望的行为,不过Timeout运算符具有重载,在超时发生时可以代替第二个事件流,而不是通过异常终止该流。
下面这个示例观察在超时发生之前的所有鼠标移动事件,在超时发生后,代码进而观察鼠标单击事件: