在 Blazor 中,`await Task.Run(StateHasChanged)` 和 `await InvokeAsync(StateHasChanged)` 有什么区别?

In Blazor what is the difference between `await Task.Run(StateHasChanged)` and `await InvokeAsync(StateHasChanged)`?

我最近继承了一个 Blazor Webassembly 应用程序,但是对 dotnet 或 Blazor 的经验很少。

一些组件使用 await Task.Run(StateHasChanged) 而不是 await InvokeAsync(StateHasChanged),我想知道这是否是故意的。

我问 await Task.Run(StateHasChanged); 在尝试使用 bUnit 渲染组件时出现以下异常:

System.InvalidOperationException The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.

将此更改为 await InvokeAsync(StateHasChanged); 允许组件在 bUnit 中呈现。但是,据我所知,将应用程序用于 await Task.Run(StateHasChanged)await InvokeAsync(StateHasChanged).

时,组件的功能相同

这两种调用方法有什么区别StateHasChanged

as far as I can tell, the component functions identically ...

没错。 Task.Run(job) 将 运行 ThreadPool 上的作业。然而,在 WebAssembly 中,没有额外的线程,主(唯一)线程迟早必须 运行 这项工作。

在 Blazor Server 中你有线程。 Task.Run() 将在那里工作,但 StateHasChanged() 必须 运行 在主线程上。也就是说

 await Task.Run(StateHasChanged)   // bug!  Don't do this.

绝对是一个错误,无处不在。它暂时在 WebAssembly 上没有引起注意。直到 Blazor Wasm 也获得线程的那一天,它才会抛出。

所以 bUnit 是正确的,修正你的代码。

请注意,在 'normal' 生命周期事件(如 OnInitialized[Async]、OnClick、OnSubmit 等)中,您根本不必使用 InvokeAsync()。 我通常只使用

  StateHasChanged();

并在外部事件(例如计时器)或线程代码(在服务器上)中

  await InvokeAsync(StateHasChanged);