You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:30:57