Blazor 中的 StateHasChanged() 与 InvokeAsync(StateHasChanged)
Posted
技术标签:
【中文标题】Blazor 中的 StateHasChanged() 与 InvokeAsync(StateHasChanged)【英文标题】:StateHasChanged() vs InvokeAsync(StateHasChanged) in Blazor 【发布时间】:2021-03-21 15:29:35 【问题描述】:我知道调用StateHasChanged()
方法会通知组件状态已更改,因此它应该重新渲染。
但是,我在其他人的代码中也看到了对 await InvokeAsync(StateHasChanged)
或 await InvokeAsync(() => StateHasChanged())
的调用,但我不太明白它与 StateHasChanged()
有何不同以及应该在哪里选择一个而不是另一个,并且 为什么。
我能找到的唯一信息是this part of the Blazor docs,上面写着:
如果必须根据外部事件(例如计时器或其他通知)更新组件,请使用 InvokeAsync 方法,该方法调度到 Blazor 的同步上下文。
我不太明白。它只是说“...调度到 Blazor 的同步上下文”,但我对此不太满意!什么是“Blazor 的同步上下文”?
我尝试在Timer
的Elapsed
事件中调用StateHasChanged()
- 而不是InvokeAsync(StateHasChanged)
,它按预期工作,没有任何问题。我应该打电话给await InvokeAsync(StateHasChanged)
吗?!如果是这样,为什么究竟是什么?我觉得这里可能有一些我不知道的重要细微差别。
我还看到了像InvokeAsync(() => InvokeAsync(Something))
这样的电话,为什么?
另外,我有时也会看到InvokeAsync()
没有await
被调用,那是怎么回事?!
【问题讨论】:
【参考方案1】:我尝试在 Timer 的 Elapsed 事件中调用 StateHasChanged() - 而不是 InvokeAsync(StateHasChanged),它按预期工作
那一定是在 WebAssembly 上。当您在 Blazor Serverside 上尝试时,我预计会出现异常。 StateHasChanged() 检查它是否在正确的线程上运行。
核心问题是渲染和调用 StateHasChanged 都必须发生在主 (UI) 线程上。 DOM 的卷影副本不是线程安全的。
主要的 Blazor 生命周期事件(OnInit、AfterRender、ButtonClick)都在该特殊线程上执行,因此在极少数情况下您需要 StateHasChanged() 可以在没有 InvokeAsync() 的情况下调用它。
Timer 是不同的,它是一个“外部事件”,所以你不能确定它会在正确的线程上执行。 InvokeAsync() 将工作委托给 Blazor 的 SynchronizationContext,这将确保它确实在主线程上运行。
但是 Blazor WebAssembly 只有 1 个线程,所以暂时外部事件也总是在主线程上运行。这意味着当你把这个 Invoke 模式弄错时,你不会注意到任何事情。直到有一天,当 Blazor Wasm 最终获得真正的线程时,您的代码将失败。与您的 Timer 实验一样。
什么是“Blazor 的同步上下文”?
在 .net 中,同步上下文决定了(之后)等待会发生什么。不同的平台有不同的设置,Blazor synccontext 很像 WinForms 和 WPF。主要是默认.ConfigureAwait(true)
:在同一个线程上恢复。
我有时会在*** Blazor Wasm 代码中看到 .ConfigureAwait(false)
。当我们在那里获得真正的线程时,它也会爆炸。可以在从 blazor 调用的服务中使用,但不能用于***方法。
最后,await InvokeAsync(StateHasChanged)
或 await InvokeAsync(() => StateHasChanged()
只是 C# 中的 lambda,与 Blazor 无关。第一种短格式效率更高。
我有时也会看到
InvokeAsync()
在没有await
的情况下被调用
这绝不是一个好主意,尽管它通常会起作用。但它比将调用方法(如 Timer 的 OnTick)设置为 async void
更好,所以从同步代码中使用它。
【讨论】:
谢谢。顺便说一句,关于你的声明And finally, await InvokeAsync(StateHasChanged or await InvokeAsync(() => StateHasChanged() is just about lambda's in C#, nothing to do with Blazor.
但我的意思是我实际上在另一个调用中看到了 InvokeAsync
调用,如 here(第 42 行)
嗯,这是一个测试,不知道它是干什么用的。它不是“真正的代码”。
哦,好的,很高兴知道。谢谢!还有一件事:你说...Until one day, when Blazor Wasm finally gets real threads, your code will fail
,真的要来了吗?还是希望 Blazor WebAssembly 始终保持单线程?这反过来意味着InvokeAsync
在 Blazor WebAssembly 中将始终有效地不相关,并且仅在 Blazor 服务器端相关。
我认为正在讨论中:github.com/dotnet/aspnetcore/discussions/22885
好的,所以还没有决定。谢谢!以上是关于Blazor 中的 StateHasChanged() 与 InvokeAsync(StateHasChanged)的主要内容,如果未能解决你的问题,请参考以下文章
何时在 Blazor 组件中调用 StateHasChanged()
Blazor 组件在 StateHasChanged() 之后未更新
我应该何时调用 StateHasChanged 以及 Blazor 何时自动拦截某些更改?