Blazor中的StateHasChanged()与InvokeAsync(StateHasChanged)

我知道调用该StateHasChanged()方法会通知组件状态已更改,因此它应该重新渲染。

但是,我也看到调用await InvokeAsync(StateHasChanged)await InvokeAsync(() => StateHasChanged())在其他人的代码中,但我不太明白它与其他人的代码有何不同StateHasChanged()以及应该在何处选择一个,以及为什么.

我能找到的唯一信息是Blazor 文档的这一部分,它说:

如果必须根据外部事件(例如计时器或其他通知)更新组件,请使用 InvokeAsync 方法,该方法分派到 Blazor 的同步上下文。

我不太明白这个。它只是说“......它调度到 Blazor 的同步上下文”,但我对此不太满意!什么是“Blazor 的同步上下文”?

我试过在 a的事件中调用StateHasChanged()- 而不是InvokeAsync(StateHasChanged)- ,它按预期工作,没有任何问题。我应该打电话吗?!如果是这样,为什么?我觉得这里可能有一些我不知道的重要细微差别。TimerElapsedawait InvokeAsync(StateHasChanged)

我也看到过类似的电话InvokeAsync(() => InvokeAsync(Something)),为什么?

另外,我有时也会看到InvokeAsync()没有调用await,那有什么关系?!

回答

我曾尝试在 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 的同步上下文很像 WinForms 和 WPF 的同步上下文。主要是,默认是.ConfigureAwait(true):在同一个线程上恢复。

我有时会.ConfigureAwait(false)在顶级 Blazor Wasm 代码中看到。当我们在那里获得真正的线程时,它也会爆炸。可以在从 blazor 调用的服务中使用,但不适用于顶级方法。

最后, await InvokeAsync(StateHasChanged)或者await InvokeAsync(() => StateHasChanged()只是关于 C# 中的 lambda,与 Blazor 无关。第一种简短形式效率更高一些。

我有时也看到InvokeAsync()没有调用await

这绝不是一个好主意,尽管它通常会奏效。但它比使调用方法(如 Timer 的 OnTick)成为 更好async void,因此从同步代码中使用它。


以上是Blazor中的StateHasChanged()与InvokeAsync(StateHasChanged)的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>