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

Blazor中ProtectedLocalStorage的SetAsync方法await时无限挂起问题

问题根源:异步操作的同步阻塞导致死锁

你的代码出现挂起的核心原因是同步代码阻塞异步操作引发的死锁,具体逻辑链如下:

  1. 直接触发点:GetValue属性的getter中使用了LocalStorage.GetAsync<int>("counter").Result——这是典型的错误写法,在Blazor Server的同步上下文(SynchronizationContext)中,.Result会阻塞当前线程等待异步任务完成,而GetAsync本身依赖该上下文执行,最终形成循环阻塞。
  2. 表象误导:点击按钮执行TestLocalStorage时,await LocalStorage.SetAsync(...)执行完成后,你调用InvokeAsync(StateHasChanged)触发组件重新渲染。此时组件会重新计算@GetValue的值,进入getter的同步阻塞逻辑,占用了Blazor的同步上下文,导致后续异步流程无法继续,看起来就像是SetAsync挂起了。

修复方案:避免同步阻塞异步操作

方案1:使用本地字段存储值,在生命周期/异步方法中加载数据

把异步获取本地存储的逻辑放到组件初始化或专用异步方法中,用本地字段存储结果,不在属性getter中执行异步操作:

@page "/"
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage

<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?"/>

<button @onclick="TestLocalStorage">@displayCounter</button>

@code {
    [Inject]
    ProtectedLocalStorage LocalStorage { get; set; }

    private int counter;
    private int displayCounter;

    protected override async Task OnInitializedAsync()
    {
        // 组件初始化时从本地存储加载值
        var storedCounter = await LocalStorage.GetAsync<int>("counter");
        if (storedCounter.Success)
        {
            counter = storedCounter.Value;
            displayCounter = counter;
        }
    }

    private async Task TestLocalStorage()
    {
        counter++;
        await LocalStorage.SetAsync("counter", counter);
        displayCounter = counter;
        // 属性更新后Blazor会自动触发渲染,无需手动调用StateHasChanged
    }
}

方案2:使用异步绑定表达式(直接绑定异步方法)

如果需要直接在UI中绑定异步数据,可以使用@await表达式调用异步方法:

@page "/"
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage

<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?"/>

<button @onclick="TestLocalStorage">@(await GetCounterValue())</button>

@code {
    [Inject]
    ProtectedLocalStorage LocalStorage { get; set; }

    private int counter;

    private async Task<int> GetCounterValue()
    {
        if (counter == 0)
            return 0;
            
        var result = await LocalStorage.GetAsync<int>("counter");
        return result.Success ? result.Value : 0;
    }

    private async Task TestLocalStorage()
    {
        counter++;
        await LocalStorage.SetAsync("counter", counter);
    }
}

你提到的对组件渲染生命周期的误解是关键——ProtectedLocalStorage的方法本身不会触发组件渲染,但InvokeAsync(StateHasChanged)会触发重新渲染,此时同步阻塞的属性getter占用上下文导致死锁。只要避免在同步代码中阻塞异步操作,改用异步方式处理数据加载和绑定,就能解决问题。

内容的提问来源于stack exchange,提问作者Paul Carlton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:55:28