【问题标题】:StateHasChanged() vs InvokeAsync(StateHasChanged) in BlazorBlazor 中的 StateHasChanged() 与 InvokeAsync(StateHasChanged)
【发布时间】:2021-03-21 15:29:35
【问题描述】:

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

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

我能找到的唯一信息是this part of the Blazor docs,上面写着:

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

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

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

我还看到了像InvokeAsync(() => InvokeAsync(Something)) 这样的电话,为什么?

另外,我有时也会看到InvokeAsync() 没有await 被调用,那是怎么回事?!

【问题讨论】:

    标签: blazor blazor-server-side blazor-webassembly


    【解决方案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 服务器端相关。
    • 好的,所以还没有决定。谢谢!
    猜你喜欢
    • 2022-01-07
    • 2020-08-11
    • 2021-09-03
    • 2021-08-02
    • 1970-01-01
    • 2021-11-07
    • 2021-01-23
    • 2020-04-22
    • 2020-08-07
    相关资源
    最近更新 更多