System.Reactive基础——通过节流和采样控制事件流
问题
在编写响应式代码时,时间传入过快是常见的问题,但是高速运行的事件流会使程序不堪重负。
解决方案
为了应付事件数据的洪流,System.Reactive提供了专门的运算符。Throttle(节流)和Sample(采样)提供了两种“驯化”高速输入事件的途径
Throttle运算符创建一个滑动的超时窗口。当事件到达时,它会重置超时窗口。当超时窗口过期时,它会发布最后一个到达窗口的事件值。
下面的示例监听鼠标移动,当且仅当鼠标静止整整1秒后,才通过Throttle运算符汇报更新:
private void button8_Click(object sender, EventArgs e)
{
Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler => MouseMove -= handler
).Select(x => x.EventArgs.GetPosition(this)).Throttle(TimeSpan.FromSeconds(1)).Subscribe(x => Trace.WriteLine(
$"{DateTime.Now.Second};Saw{x.X + x.Y}"));
}
Throttle运算符常常用在类似自动填充的情况下,基当用户正在文本框内输入文字时,希望在用户停下来之前就进行实际的查询。
Sample的工作方式有所不同。他创建一个常规的超时时段,并在每次超时过期时,发布该窗口内最近的值。如果在采样时段内未能收到值,则不会发布该时段的任何结果。
下面的示例捕获鼠标移动并以1秒为间隔对移动进行采样,和Throttle示例不同,Sample示例并不需要通过静止鼠标来查看数据:
Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler => MouseMove -= handler).Select(x => x.EventArgs.Location).Sample(TimeSpan.FromSeconds(1)).Subscribe(x => Trace.WriteLine($"{DateTime.Now.Second};Saw {x.X + x.Y}"));
讨论
对“驯化”海量的输入而言,节流和采样都是必要的工具,别忘了,通过标准LINQ Where运算符,还能轻松地进行过滤。不妨把Throttle和Sample看作与Where类似的运算符,只不过这两者根据时间窗口而不是事件数据执行过滤。这三种运算符都能一个字的方式“驯化”高速的输入流。