Skip to content

Commit 1ae8579

Browse files
authored
feat(ThrottleDispatcher): refine code improve readability (#6379)
* refactor: 精简代码逻辑 * test: 更新单元测试 * test: 精简命名空间
1 parent e3d081e commit 1ae8579

File tree

2 files changed

+61
-88
lines changed

2 files changed

+61
-88
lines changed

src/BootstrapBlazor/Services/ThrottleDispatcher.cs

Lines changed: 23 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,13 @@ namespace BootstrapBlazor.Components;
1010
/// </summary>
1111
public class ThrottleDispatcher(ThrottleOptions options)
1212
{
13-
private readonly object _locker = new();
14-
private Task? _lastTask;
1513
private DateTime? _invokeTime;
16-
private bool _busy;
1714

1815
/// <summary>
1916
/// 判断是否等待方法
2017
/// </summary>
2118
/// <returns></returns>
22-
protected virtual bool ShouldWait() => _busy || _invokeTime.HasValue && (DateTime.UtcNow - _invokeTime.Value) < options.Interval;
19+
protected virtual bool ShouldWait() => _invokeTime.HasValue && (DateTime.UtcNow - _invokeTime.Value) < options.Interval;
2320

2421
/// <summary>
2522
/// 异步限流方法
@@ -32,83 +29,53 @@ public class ThrottleDispatcher(ThrottleOptions options)
3229
/// 同步限流方法
3330
/// </summary>
3431
/// <param name="action">同步回调方法</param>
35-
/// <param name="cancellationToken">取消令牌</param>
36-
public void Throttle(Action action, CancellationToken cancellationToken = default)
32+
/// <param name="token">取消令牌</param>
33+
public void Throttle(Action action, CancellationToken token = default)
3734
{
3835
var task = InternalThrottleAsync(() => Task.Run(() =>
3936
{
4037
action();
4138
return Task.CompletedTask;
42-
}, cancellationToken), cancellationToken);
43-
Wait();
44-
return;
45-
46-
[ExcludeFromCodeCoverage]
47-
void Wait()
39+
}, CancellationToken.None), token);
40+
try
4841
{
49-
try
50-
{
51-
task.Wait(cancellationToken);
52-
}
53-
catch (AggregateException ex)
54-
{
55-
if (ex.InnerException is not null)
56-
{
57-
throw ex.InnerException;
58-
}
59-
}
60-
catch (Exception)
61-
{
62-
throw;
63-
}
42+
task.Wait(token);
43+
}
44+
catch (Exception)
45+
{
46+
throw;
6447
}
48+
return;
6549
}
6650

67-
/// <summary>
68-
/// 任务实例
69-
/// </summary>
70-
protected Task LastTask => _lastTask ?? Task.CompletedTask;
71-
7251
/// <summary>
7352
/// 限流异步方法
7453
/// </summary>
7554
/// <param name="function">异步回调方法</param>
7655
/// <param name="cancellationToken">取消令牌</param>
77-
private Task InternalThrottleAsync(Func<Task> function, CancellationToken cancellationToken = default)
56+
private async Task InternalThrottleAsync(Func<Task> function, CancellationToken cancellationToken = default)
7857
{
7958
if (ShouldWait())
8059
{
81-
return LastTask;
60+
return;
8261
}
8362

84-
lock (_locker)
63+
_invokeTime = DateTime.UtcNow;
64+
65+
try
8566
{
86-
if (ShouldWait())
67+
await function();
68+
if (options.DelayAfterExecution)
8769
{
88-
return LastTask;
70+
_invokeTime = DateTime.UtcNow;
8971
}
90-
91-
_busy = true;
92-
_invokeTime = DateTime.UtcNow;
93-
_lastTask = function();
94-
_lastTask.ContinueWith(_ =>
95-
{
96-
if (options.DelayAfterExecution)
97-
{
98-
_invokeTime = DateTime.UtcNow;
99-
}
100-
_busy = false;
101-
}, cancellationToken);
102-
72+
}
73+
catch
74+
{
10375
if (options.ResetIntervalOnException)
10476
{
105-
_lastTask.ContinueWith((_, _) =>
106-
{
107-
_lastTask = null;
108-
_invokeTime = null;
109-
}, cancellationToken, TaskContinuationOptions.OnlyOnFaulted);
77+
_invokeTime = null;
11078
}
111-
return LastTask;
11279
}
11380
}
11481
}

test/UnitTest/Services/ThrottleTest.cs

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,21 @@ public async Task ResetIntervalOnException_Ok()
8181
var dispatcher = factory.GetOrCreate("Error", new ThrottleOptions() { ResetIntervalOnException = true });
8282

8383
var count = 0;
84-
await Assert.ThrowsAnyAsync<InvalidOperationException>(() => dispatcher.ThrottleAsync(() =>
84+
await dispatcher.ThrottleAsync(() =>
8585
{
8686
count++;
87-
throw new InvalidOperationException();
88-
}));
87+
throw new Exception();
88+
});
89+
Assert.Equal(1, count);
8990

90-
Assert.ThrowsAny<InvalidOperationException>(() => dispatcher.Throttle(() => throw new InvalidOperationException()));
91+
dispatcher.Throttle(() => throw new InvalidOperationException());
9192

9293
// 发生错误后可以立即执行下一次任务,不限流
9394
dispatcher.Throttle(() =>
9495
{
9596
count++;
9697
});
98+
Assert.Equal(2, count);
9799
}
98100

99101
[Fact]
@@ -104,16 +106,21 @@ public async Task Cancel_Ok()
104106

105107
var cts = new CancellationTokenSource();
106108
cts.Cancel();
107-
Assert.ThrowsAny<OperationCanceledException>(() => dispatcher.Throttle(async () =>
109+
var ex = await Assert.ThrowsAsync<OperationCanceledException>(() =>
108110
{
109-
await Task.Delay(300);
110-
}, cts.Token));
111+
dispatcher.Throttle(() =>
112+
{
113+
114+
}, cts.Token);
115+
return Task.CompletedTask;
116+
});
117+
Assert.NotNull(ex);
111118

112119
cts = new CancellationTokenSource(100);
113-
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => dispatcher.ThrottleAsync(async () =>
120+
await dispatcher.ThrottleAsync(async () =>
114121
{
115122
await Task.Delay(300);
116-
}, cts.Token));
123+
}, cts.Token);
117124
}
118125

119126
[Fact]
@@ -125,38 +132,37 @@ public void Clear()
125132
factory.Clear("Clear");
126133
}
127134

128-
[Fact]
129-
public void LatTask_Ok()
130-
{
131-
var dispatch = new MockDispatcher(new ThrottleOptions());
132-
Assert.NotNull(dispatch.TestLastTask());
133-
}
134-
135135
[Fact]
136136
public void ShouldWait_Ok()
137137
{
138-
var dispatch = new MockDispatcher(new ThrottleOptions());
138+
var dispatch = new ThrottleDispatcher(new ThrottleOptions());
139139
var count = 0;
140140
dispatch.Throttle(() => count++);
141-
Assert.Equal(0, count);
141+
Assert.Equal(1, count);
142+
dispatch.Throttle(() => count++);
143+
Assert.Equal(1, count);
142144
}
143145

144-
class MockDispatcher(ThrottleOptions options) : ThrottleDispatcher(options)
146+
[Fact]
147+
public async Task MultipleThread_ThrottleAsync_Ok()
145148
{
146-
public Task TestLastTask()
149+
var count = 0;
150+
var dispatch = new ThrottleDispatcher(new ThrottleOptions()
147151
{
148-
return LastTask;
149-
}
150-
151-
private int count = 0;
152-
153-
/// <summary>
154-
/// <inheritdoc/>
155-
/// </summary>
156-
/// <returns></returns>
157-
protected override bool ShouldWait()
152+
Interval = TimeSpan.FromMilliseconds(100),
153+
DelayAfterExecution = true
154+
});
155+
var tasks = Enumerable.Range(1, 2).Select(i => dispatch.ThrottleAsync(() =>
158156
{
159-
return count++ == 1;
160-
}
157+
count++;
158+
return Task.CompletedTask;
159+
})).ToList();
160+
tasks.Add(dispatch.ThrottleAsync(async () =>
161+
{
162+
await Task.Delay(120);
163+
count++;
164+
}));
165+
await Task.WhenAll(tasks);
166+
Assert.Equal(1, count);
161167
}
162168
}

0 commit comments

Comments
 (0)