CSharp 去抖和节流
2021-04-20

去抖和节流,是前端频繁提及的概念。因为前端容易触及性能问题,比如窗口滑动、鼠标重复点击等等,如果没有去抖/节流/防重复提交这些操作,页面交互极有可能变得肉眼可见的卡顿。

现在 CSharp 编程中遇到类似情况,需要将同样的原理应用于后台编程。

一般情况下,我们可以使用第三方库,比如 Reactive Extension (类似于前端的 RxJS )。但是,我就只是要一个去抖和节流的功能,没必要引入一个第三方库吧。现实中项目如果要引入第三方库可能会引入各种问题。既然如此,何不重复造个轮子 😂

Talk is cheap, show me the code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/// <summary>
/// <para>Provides Debounce() and Throttle() methods.
/// Use these methods to ensure that events aren't handled too frequently.</para>
///
/// <para>Throttle() ensures that events are throttled by the interval specified.
/// Only the last event in the interval sequence of events fires.</para>
///
/// <para>Debounce() fires an event only after the specified interval has passed
/// in which no other pending event has fired. Only the last event in the
/// sequence is fired.</para>
/// </summary>
public class DebounceDispatcher
{
private CancellationTokenSource debouncer;
/// <summary>
/// <para>Debounce an event by resetting the event timeout every time the event is
/// fired. The behavior is that the Action passed is fired only after events
/// stop firing for the given timeout period.</para>
///
/// <para>Use Debounce when you want events to fire only after events stop firing
/// after the given interval timeout period.</para>
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
public void Debounce(int interval, Action<object> action, object param = null)
{
if (debouncer != null && !debouncer.IsCancellationRequested)
{
debouncer.Cancel();
}
try
{
debouncer = new CancellationTokenSource();
var t = Task.Delay(interval, debouncer.Token);
t.ContinueWith((p) =>
{
if (p.Status == TaskStatus.RanToCompletion)
action.Invoke(param);
});
t.Start();
}
catch (Exception)
{
// duplicated invokes, the previous one was cancelled
System.Console.WriteLine($"[{DateTime.Now}]: task was cancelled");
}
}
private long throttler = 0;
/// <summary>
/// <para>This method throttles events by allowing only 1 event to fire for the given
/// timeout period. Only the last event fired is handled - all others are ignored.
/// Throttle will fire events every timeout ms even if additional events are pending.</para>
///
/// <para>Use Throttle where you need to ensure that events fire at given intervals.</para>
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
public void Throttle(int interval, Action<object> action, object param = null)
{
if (throttler == 0 || (DateTime.Now.Ticks - throttler) / 10000 >= interval)
{
action.Invoke(param);
throttler = DateTime.Now.Ticks;
}
}
}
  • 节流很简单,就不过多解释了;
  • 去抖,此处用到了三个关键函数: Task.Delay (类似于 js 中 setTimeout )、Task.ContinueWith (类似于 js 中的 Promise.then,不同的是后者接受两个参数,分别是成功回调和错误回调,而前者仅有一个回调,成功和错误都在一个里面)、CancellationTokenSource.Cancel

参考链接

本文链接:
content_copy https://zxs66.github.io/2021/04/20/CSharp-debounce-and-throttle/