Blazor防抖搜索组件触发EventCallback时Dispatcher异常解决方案咨询
解决Blazor防抖搜索组件的Dispatcher跨线程异常问题
问题核心
你遇到的异常是因为System.Timers.Timer的Elapsed事件在后台线程触发,而Blazor的EventCallback必须在UI线程执行,直接调用会触发跨线程访问UI上下文的异常。
解决方案1:通过组件InvokeAsync调度到UI线程
修改DebounceTimerElapsed方法,利用Blazor组件自带的InvokeAsync方法,将EventCallback的调用调度回UI线程:
<input type="search" placeholder="Search" @oninput=@((e) => SearchChanged(e)) /> @implements IDisposable @code { [Parameter] public EventCallback<string> OnSearchChanged { get; set; } private System.Timers.Timer? _debounceTimer; string Text = string.Empty; public void SearchChanged(ChangeEventArgs e) { Text = e.Value == null ? string.Empty : e.Value.ToString()!; _debounceTimer?.Stop(); _debounceTimer?.Dispose(); _debounceTimer = new System.Timers.Timer(200); _debounceTimer.Elapsed += DebounceTimerElapsed; _debounceTimer.Start(); } private void DebounceTimerElapsed(object? sender, EventArgs e) { _debounceTimer?.Dispose(); _debounceTimer = null; // 将EventCallback调用调度到UI线程 _ = InvokeAsync(() => OnSearchChanged.InvokeAsync(Text)); } // 组件销毁时清理Timer public void Dispose() { _debounceTimer?.Dispose(); } }
说明:
InvokeAsync会把传入的委托放入Blazor的Dispatcher队列,确保在UI线程执行,避免跨线程异常。- 加上
@implements IDisposable并实现Dispose方法,防止Timer内存泄漏。
解决方案2:改用Task.Delay实现防抖(更推荐)
放弃System.Timers.Timer,改用Task.Delay结合CancellationToken实现防抖,更符合Blazor异步编程模型,无需处理线程切换问题:
<input type="search" placeholder="Search" @oninput=@((e) => SearchChanged(e)) /> @implements IDisposable @code { [Parameter] public EventCallback<string> OnSearchChanged { get; set; } private CancellationTokenSource? _debounceCts; string Text = string.Empty; public async void SearchChanged(ChangeEventArgs e) { Text = e.Value == null ? string.Empty : e.Value.ToString()!; // 取消之前的延迟任务 _debounceCts?.Cancel(); _debounceCts?.Dispose(); _debounceCts = new CancellationTokenSource(); try { // 等待防抖间隔 await Task.Delay(200, _debounceCts.Token); // await后自动回到UI线程上下文,直接调用EventCallback即可 await OnSearchChanged.InvokeAsync(Text); } catch (OperationCanceledException) { // 防抖触发,取消任务,忽略异常 } finally { _debounceCts.Dispose(); _debounceCts = null; } } public void Dispose() { _debounceCts?.Dispose(); } }
说明:
Task.Delay配合CancellationToken可以在用户输入新内容时取消之前的延迟任务,实现防抖效果。- Blazor的异步上下文会在
await后自动回到UI线程,因此直接调用OnSearchChanged.InvokeAsync不会触发跨线程异常。 - 同样需要实现
IDisposable清理CancellationTokenSource,避免资源泄漏。
内容的提问来源于stack exchange,提问作者flip
相关产品推荐
相关产品推荐

