Blazor InvokeAsync 与 await InvokeAsync

问题描述

编辑:为了澄清起见,这是在 Blazor 服务器应用程序中

我对 InvokeAsync 的正确用法和更新 UI 感到困惑。该文档有多种用法,但没有真正解释等待或不等待的原因。我也看到了一些相互矛盾的线索,但没有太多证据支持他们的推理。

让所有方法异步以等待 InvokeAsync(StateHasChanged) 似乎是错误的,我在某处读到引入 InvokeAsync 的原因是为了防止在任何地方都需要异步代码。但是在什么情况下我可能想要等待它?

等待:

https://docs.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-5.0&viewFallbackFrom=aspnetcore-3.0#invoke-component-methods-externally-to-update-state

非等待丢弃:

https://docs.microsoft.com/en-us/aspnet/core/blazor/components/rendering?view=aspnetcore-5.0

以下是我见过的不同用法的一些示例,如果有人能解释或分享有关它们之间某些差异的信息的链接,那就太好了(谢谢!)

public void IncrementCounter()
{
    _counter++;
    InvokeAsync(StateHasChanged);
}
public void IncrementCounter()
{
    InvokeAsync(() => 
    {
        _counter++;
        StateHasChanged);
    }
}
public async Task IncrementCounter()
{
    _counter++;
    await InvokeAsync(StateHasChanged);
}
public async Task IncrementCounter()
{
    await InvokeAsync(() =>
    {
        _counter++;
        StateHasChanged();
    });
}

解决方法

IncrementCounter(一个 ButtonClick 处理程序)是错误的东西——它总是在 SyncContext 线程上运行,并且总是可以使用没有 Invoke 的普通 StateHasChanged()。

让我们来看看 Timer 事件。 Threading.Timer 类不支持异步处理程序,因此您可以在未指定线程上的 void Tick() { ... } 中运行。

这里确实需要 InvokeAsync(StateHasChanged)。您可以将 Tick 方法设为 async void 以等待 InvokeAsync,但这会给出错误的信号。使用不带 await 的 InvokeAsync 危害较小。

void Tick()  // possibly threaded event handler
{
   _counter++;
   InvokeAsync(StateHasChanged);  // fire-and-forget mode
}

但是当你在一个异步方法中并且仍然需要 InvokeAsync 时,等待它会更简洁,因为你可以。

async Task SomeService()  
{
   _counter++;
   await InvokeAsync(StateHasChanged); 
}