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

Blazor Server页面加载时使用ContinueWith的技术疑问

Blazor Server 异步加载组件状态的疑问解答

你开发Blazor Server应用时,希望页面加载时同时调用API加载Item1和Item2,各自完成后立即显示,给出的实现代码如下:

protected override async Task OnInitializedAsync()
{
    var t1 = _service.GetItem1().ContinueWith(t => {
        Item1Loaded = true;
        Item1State = t.Result; // 注:原代码此处疑似笔误,应为Item1State
        await InvokeAsync(StateHasChanged);
    });

    var t2 = _service.GetItem2().ContinueWith(t => {
        Item2Loaded = true;
        Item2State = t.Result;
        await InvokeAsync(StateHasChanged);
    });
}

针对你的三个疑问,解答如下:

1. 用户离开页面后的任务取消与组件销毁问题

需要手动取消这些任务,Blazor框架不会自动处理未完成的异步任务。

  • 当用户离开页面,组件会触发Dispose(若实现IDisposable接口),此时未完成的任务后续修改状态变量、调用StateHasChanged可能引发异常——因为组件实例已被销毁,相关上下文不再有效。
  • 解决方法:在组件中声明CancellationTokenSource,将令牌传递给API调用,在组件销毁时取消令牌。示例:
private CancellationTokenSource _cts;

protected override async Task OnInitializedAsync()
{
    _cts = new CancellationTokenSource();
    var token = _cts.Token;

    var t1 = _service.GetItem1(token).ContinueWith(t => {
        if (!token.IsCancellationRequested)
        {
            Item1Loaded = true;
            Item1State = t.Result;
            await InvokeAsync(StateHasChanged);
        }
    }, token);

    var t2 = _service.GetItem2(token).ContinueWith(t => {
        if (!token.IsCancellationRequested)
        {
            Item2Loaded = true;
            Item2State = t.Result;
            await InvokeAsync(StateHasChanged);
        }
    }, token);
}

public void Dispose()
{
    _cts?.Cancel();
    _cts?.Dispose();
}

注意需让组件实现IDisposable接口。

2. 同步上下文与InvokeAsync的作用

是的,必须确保回到Blazor的同步上下文执行状态修改和StateHasChanged调用。

  • Blazor Server的组件运行在关联用户SignalR连接的特定同步上下文,跨线程修改组件状态会导致线程安全问题。
  • InvokeAsync会将传入的委托调度回组件的同步上下文执行,你代码中使用它的方式是正确的,能避免跨线程访问组件状态的异常。

3. ContinueWith与async/await的选择

这里用ContinueWith能实现需求,但用async/await的写法更易读、更符合现代C#异步编程规范,且同样能做到“任务完成即更新状态”。

  • 你认为async/await无法按任务完成顺序执行延续操作是误解——async/await可以通过并行启动多个异步任务,各自独立await并处理结果。比如可以用以下代码替代:
private CancellationTokenSource _cts;

protected override async Task OnInitializedAsync()
{
    _cts = new CancellationTokenSource();
    var token = _cts.Token;

    // 并行启动两个异步任务
    var task1 = LoadItem1(token);
    var task2 = LoadItem2(token);

    // 可选:若不需要等待全部完成,可省略此句
    await Task.WhenAll(task1, task2);
}

private async Task LoadItem1(CancellationToken token)
{
    var result = await _service.GetItem1(token);
    if (!token.IsCancellationRequested)
    {
        Item1Loaded = true;
        Item1State = result;
        await InvokeAsync(StateHasChanged);
    }
}

private async Task LoadItem2(CancellationToken token)
{
    var result = await _service.GetItem2(token);
    if (!token.IsCancellationRequested)
    {
        Item2Loaded = true;
        Item2State = result;
        await InvokeAsync(StateHasChanged);
    }
}

这种写法逻辑清晰,更易维护,也避免了ContinueWith需要手动处理任务状态、调度上下文等细节问题。

内容的提问来源于stack exchange,提问作者Ben Zuill-Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 23:10:30