diff --git a/src/BootstrapBlazor/Services/ThrottleDispatcher.cs b/src/BootstrapBlazor/Services/ThrottleDispatcher.cs
index 01a92d94bcc..90ee9385812 100644
--- a/src/BootstrapBlazor/Services/ThrottleDispatcher.cs
+++ b/src/BootstrapBlazor/Services/ThrottleDispatcher.cs
@@ -10,16 +10,13 @@ namespace BootstrapBlazor.Components;
///
public class ThrottleDispatcher(ThrottleOptions options)
{
- private readonly object _locker = new();
- private Task? _lastTask;
private DateTime? _invokeTime;
- private bool _busy;
///
/// 判断是否等待方法
///
///
- protected virtual bool ShouldWait() => _busy || _invokeTime.HasValue && (DateTime.UtcNow - _invokeTime.Value) < options.Interval;
+ protected virtual bool ShouldWait() => _invokeTime.HasValue && (DateTime.UtcNow - _invokeTime.Value) < options.Interval;
///
/// 异步限流方法
@@ -32,83 +29,53 @@ public class ThrottleDispatcher(ThrottleOptions options)
/// 同步限流方法
///
/// 同步回调方法
- /// 取消令牌
- public void Throttle(Action action, CancellationToken cancellationToken = default)
+ /// 取消令牌
+ public void Throttle(Action action, CancellationToken token = default)
{
var task = InternalThrottleAsync(() => Task.Run(() =>
{
action();
return Task.CompletedTask;
- }, cancellationToken), cancellationToken);
- Wait();
- return;
-
- [ExcludeFromCodeCoverage]
- void Wait()
+ }, CancellationToken.None), token);
+ try
{
- try
- {
- task.Wait(cancellationToken);
- }
- catch (AggregateException ex)
- {
- if (ex.InnerException is not null)
- {
- throw ex.InnerException;
- }
- }
- catch (Exception)
- {
- throw;
- }
+ task.Wait(token);
+ }
+ catch (Exception)
+ {
+ throw;
}
+ return;
}
- ///
- /// 任务实例
- ///
- protected Task LastTask => _lastTask ?? Task.CompletedTask;
-
///
/// 限流异步方法
///
/// 异步回调方法
/// 取消令牌
- private Task InternalThrottleAsync(Func function, CancellationToken cancellationToken = default)
+ private async Task InternalThrottleAsync(Func function, CancellationToken cancellationToken = default)
{
if (ShouldWait())
{
- return LastTask;
+ return;
}
- lock (_locker)
+ _invokeTime = DateTime.UtcNow;
+
+ try
{
- if (ShouldWait())
+ await function();
+ if (options.DelayAfterExecution)
{
- return LastTask;
+ _invokeTime = DateTime.UtcNow;
}
-
- _busy = true;
- _invokeTime = DateTime.UtcNow;
- _lastTask = function();
- _lastTask.ContinueWith(_ =>
- {
- if (options.DelayAfterExecution)
- {
- _invokeTime = DateTime.UtcNow;
- }
- _busy = false;
- }, cancellationToken);
-
+ }
+ catch
+ {
if (options.ResetIntervalOnException)
{
- _lastTask.ContinueWith((_, _) =>
- {
- _lastTask = null;
- _invokeTime = null;
- }, cancellationToken, TaskContinuationOptions.OnlyOnFaulted);
+ _invokeTime = null;
}
- return LastTask;
}
}
}
diff --git a/test/UnitTest/Services/ThrottleTest.cs b/test/UnitTest/Services/ThrottleTest.cs
index f25c2c53459..b2235a2d2c7 100644
--- a/test/UnitTest/Services/ThrottleTest.cs
+++ b/test/UnitTest/Services/ThrottleTest.cs
@@ -81,19 +81,21 @@ public async Task ResetIntervalOnException_Ok()
var dispatcher = factory.GetOrCreate("Error", new ThrottleOptions() { ResetIntervalOnException = true });
var count = 0;
- await Assert.ThrowsAnyAsync(() => dispatcher.ThrottleAsync(() =>
+ await dispatcher.ThrottleAsync(() =>
{
count++;
- throw new InvalidOperationException();
- }));
+ throw new Exception();
+ });
+ Assert.Equal(1, count);
- Assert.ThrowsAny(() => dispatcher.Throttle(() => throw new InvalidOperationException()));
+ dispatcher.Throttle(() => throw new InvalidOperationException());
// 发生错误后可以立即执行下一次任务,不限流
dispatcher.Throttle(() =>
{
count++;
});
+ Assert.Equal(2, count);
}
[Fact]
@@ -104,16 +106,21 @@ public async Task Cancel_Ok()
var cts = new CancellationTokenSource();
cts.Cancel();
- Assert.ThrowsAny(() => dispatcher.Throttle(async () =>
+ var ex = await Assert.ThrowsAsync(() =>
{
- await Task.Delay(300);
- }, cts.Token));
+ dispatcher.Throttle(() =>
+ {
+
+ }, cts.Token);
+ return Task.CompletedTask;
+ });
+ Assert.NotNull(ex);
cts = new CancellationTokenSource(100);
- await Assert.ThrowsAnyAsync(() => dispatcher.ThrottleAsync(async () =>
+ await dispatcher.ThrottleAsync(async () =>
{
await Task.Delay(300);
- }, cts.Token));
+ }, cts.Token);
}
[Fact]
@@ -125,38 +132,37 @@ public void Clear()
factory.Clear("Clear");
}
- [Fact]
- public void LatTask_Ok()
- {
- var dispatch = new MockDispatcher(new ThrottleOptions());
- Assert.NotNull(dispatch.TestLastTask());
- }
-
[Fact]
public void ShouldWait_Ok()
{
- var dispatch = new MockDispatcher(new ThrottleOptions());
+ var dispatch = new ThrottleDispatcher(new ThrottleOptions());
var count = 0;
dispatch.Throttle(() => count++);
- Assert.Equal(0, count);
+ Assert.Equal(1, count);
+ dispatch.Throttle(() => count++);
+ Assert.Equal(1, count);
}
- class MockDispatcher(ThrottleOptions options) : ThrottleDispatcher(options)
+ [Fact]
+ public async Task MultipleThread_ThrottleAsync_Ok()
{
- public Task TestLastTask()
+ var count = 0;
+ var dispatch = new ThrottleDispatcher(new ThrottleOptions()
{
- return LastTask;
- }
-
- private int count = 0;
-
- ///
- ///
- ///
- ///
- protected override bool ShouldWait()
+ Interval = TimeSpan.FromMilliseconds(100),
+ DelayAfterExecution = true
+ });
+ var tasks = Enumerable.Range(1, 2).Select(i => dispatch.ThrottleAsync(() =>
{
- return count++ == 1;
- }
+ count++;
+ return Task.CompletedTask;
+ })).ToList();
+ tasks.Add(dispatch.ThrottleAsync(async () =>
+ {
+ await Task.Delay(120);
+ count++;
+ }));
+ await Task.WhenAll(tasks);
+ Assert.Equal(1, count);
}
}