Blazor Timer调用异步API任务来更新UI



我正在Blazor服务器端页面中设置计时器。目标是每x秒调用一次API,并根据返回值更新UI。

我得到了这个代码:

private string Time { get; set; }
protected override void OnInitialized()
{
    var timer = new System.Threading.Timer((_) =>
    {
        Time = DateTime.Now.ToString();
        InvokeAsync(() =>
        {
            StateHasChanged();
        });
    }, null, 0, 1000);
    base.OnInitialized();
}

这个效果很好。UI每秒都会使用新的时间值进行更新。然而,我不知道如何调用异步任务来获取值。我想更换线路:

Time = DateTime.Now.ToString();

使用一行调用以下函数:

private async Task<string> GetValue()
{
    var result = await _api.GetAsync<StringDto>("/api/GetValue");
    return result.Text;
}

我试过这条线:

Time = GetValue().Result;

但我收到以下错误:

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

我需要做些什么来调用异步方法?

谢谢大家!

您可能不想调用((GetValue((,这将毫无意义。你可以实现这样的定时器:

System.Threading.Timer timer;
protected override void OnInitialized()
{
    timer = new System.Threading.Timer(async _ =>  // async void
    {
        Time = await GetValue();
        // we need StateHasChanged() because this is an async void handler
        // we need to Invoke it because we could be on the wrong Thread          
        await InvokeAsync(StateHasChanged);
    }, null, 0, 1000);
}

我用了一个字段来存储Timer,因为你应该处理它,把它添加到Razor部分:

@implements IDisposable

这个到代码:

public void Dispose()
{
    timer?.Dispose();
}

在.NET 6中,您可以按如下方式使用PeriodicTimer类

私有字符串时间{get;set;}

     protected override async void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        using var periodicTimer = new PeriodicTimer(TimeSpan.FromMinutes(10));
        while (await periodicTimer.WaitForNextTickAsync())
        {
          Time=   await GetValue();
            await InvokeAsync(StateHasChanged);
        }
    }
}
     

运行它而不会永远束缚生命周期方法:

@implements IDisposable
PeriodicTimer periodicTimer = new (TimeSpan.FromMinutes(10));
protected override void OnInitialized()
{
    RunTimer();  // fire-and-forget
}
async void RunTimer()
{
    while (await periodicTimer.WaitForNextTickAsync()) { .... }    
}
public void Dispose()
{
   periodicTimer?.Dispose();
}

试试这个代码:

  private string Time { get; set; }
protected override void OnInitialized()
{
    base.OnInitialized();
    var timer = new System.Threading.Timer((_) =>
    {
        InvokeAsync( async ()  =>
        {
            Time = await GetValue();
            StateHasChanged();
        });
    }, null, 0, 1000);
}

您的GetValue方法应该是:

private async Task<string> GetValue()
{
    return await _api.GetAsync<StringDto>("/api/GetValue");
  
}

最新更新