Blazor使用Blazored.LocalStorage首次访问触发TaskCanceledException如何解决
问题解答
异常触发原因
- 核心原因是你使用了
async void修饰OnAfterRender方法:Blazor不会追踪async void方法的执行状态,当你调用_localStorage.GetItemAsStringAsync时,本质是发起JS互操作请求,此时JS运行时上下文/Blazor与客户端的通信通道还未完全就绪,JSRuntime会直接取消未就绪上下文的调用请求,抛出TaskCanceledException。 - 次要触发场景:如果你的应用开启了服务端预渲染,预渲染阶段不存在浏览器JS运行环境,所有JS互操作调用都会被直接取消;如果是Blazor Server场景,首次触发
OnAfterRender时SignalR连接可能还未完成握手,也会触发该异常。 - 版本适配问题:.NET Core 3.1对应的早期版本Blazored.LocalStorage未做JS模块加载的等待逻辑,首次调用时依赖的JS代码还未注入到页面,也会导致调用被取消。
稳定解决方案
1. 优先替换异步生命周期方法
不要使用async void的OnAfterRender,改用Blazor官方提供的异步生命周期重载OnAfterRenderAsync,Blazor会自动追踪该方法的执行上下文,确保异步调用的基础环境就绪:
protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { await base.OnAfterRenderAsync(firstRender); return; } var results = await _localStorage.GetItemAsStringAsync("MyData"); // 执行业务逻辑 await base.OnAfterRenderAsync(firstRender); }
2. 增加调用重试机制
针对JS上下文未就绪的场景,对本地存储调用增加有限次数的重试逻辑,规避初始化时序问题:
private async Task<string> GetLocalStorageWithRetryAsync(string key, int retryTimes = 3, int delayMs = 100) { var attempt = 0; while (attempt < retryTimes) { try { return await _localStorage.GetItemAsStringAsync(key); } catch (TaskCanceledException) { attempt++; if (attempt >= retryTimes) throw; await Task.Delay(delayMs); } } return null; }
在OnAfterRenderAsync中直接调用该封装方法即可。
3. 预渲染场景适配
如果你的应用开启了服务端预渲染,需要增加浏览器环境判断,仅在客户端运行时执行本地存储操作:
protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender || !OperatingSystem.IsBrowser()) { await base.OnAfterRenderAsync(firstRender); return; } // 读取本地存储逻辑 await base.OnAfterRenderAsync(firstRender); }
4. 升级依赖包版本
将Blazored.LocalStorage升级到适配.NET Core 3.1的最新兼容版本(v4.2.0为支持.NET Core 3.1的最后一个版本),该版本已经修复了JS模块初始化时序导致的调用取消问题。
内容的提问来源于stack exchange,提问作者Xaphann
相关产品推荐
相关产品推荐

