【问题标题】:StateHasChanged not execute on async call Blazor Server AppStateHasChanged 不在异步调用 Blazor 服务器应用程序上执行
【发布时间】:2021-10-18 06:45:02
【问题描述】:

当用户按下特定按钮时,我需要在按钮内显示动画并禁用它(调用的方法需要几秒钟才能完成)。 这是我的html:

 <div class="row">
            <div class="col-lg-4">
                @if (IsDownloading)
                {
                    <button class="btn btn-primary float-right search disabled" disabled><span class='fa-left fas fa-sync-alt spinning'></span>Downloading...</button>
                }
                else
                {
                    <button id="REC_PDF" class="btn btn-primary" @onclick="@(()=>getPDF())">Download Labels</button>
                }
                
            </div>
        </div>

这是我的代码:

protected bool IsDownloading { get; set; }

public async Task getPDF()
{
    IsDownloading = true;
    StateHasChanged();

    await generate_pdf(labels);

    IsDownloading = false;
    StateHasChanged();
}

private async Task generate_pdf(List<Tuple<string, string, string, string>> filtered_label)
{
    loading = true;
    string PDF_PATH = "./PDF/" + CurrentValue;
    foreach (Tuple<string,string,string,string> label in filtered_label)
    {

        if (!Directory.Exists(PDF_PATH))
        {
            Directory.CreateDirectory(PDF_PATH);
        }
.......

代码正在运行,但由 StateHasChanged() 调用的页面呈现;当 getPDF 方法(以及随之而来的 generate_PDF)完全执行时执行 2 次。

我需要在第一次调用时呈现页面以显示 css 加载动画。

(正如我在其他帖子中所建议的那样,我使用了 InvokeAsync(() => StateHasChanged()); 但不起作用)

谢谢。

【问题讨论】:

  • @onclick="@(()=&gt;getPDF())" @onclick="getPDF" 是正确的方法...但不是您问题的原因。

标签: blazor blazor-server-side


【解决方案1】:

试试这个修改:

public async Task getPDF()
{
    if (IsDownloading) return;

    IsDownloading = true;
    StateHasChanged();

    await generate_pdf(labels);
}

然后在 generate_pdf 的末尾(最好在 try/finally 块中)添加:

    IsDownloading = false;
    StateHasChanged();

【讨论】:

    【解决方案2】:

    问题是generate_pdf() 不是异步的。添加async 是不够的。你需要一些异步的东西来启用渲染。

    由于这是一个 UI 问题,我建议在顶层解决它:

    public async Task getPDF()
    {
        IsDownloading = true;
        //StateHasChanged();  // not needed at start
        await task.Delay(1);  // allow the UI to update    
    
        try
        {
          await generate_pdf(labels);     
        }
        finally
        {
          IsDownloading = false;         
        }
        //StateHasChanged();  // not needed at the end
    }
    

    当 generate_pdf() 本身不执行任何 await 时,您不妨将其设为普通的 void 方法。

    【讨论】:

      【解决方案3】:

      点击事件在 Blazor 中是这样处理的:

      var task = InvokeAsync(EventMethod);
      StateHasChanged();
      if (!task.IsCompleted)
      {
          await task;
          StateHasChanged();
      }
      

      注意StateHasChanged 实际上并没有重新渲染组件,它只是在渲染队列上堆叠一个RenderFragment 委托。

      所有代码都在“UI”线程上运行,因此通过调用StateHasChanged 排队的渲染事件只会被执行:

      • 在事件方法产生控制权时
      • 如果最后没有yield发生(因为Task块内只有同步代码)。

      我在您的generate_pdf 中看不到任何代码。

      尝试以下方法:

      public async Task getPDF()
      {
          IsDownloading = true;
          await Task.Yield();
      
          await generate_pdf(labels);
      
          IsDownloading = false;
      }
      

      注意:Directory.CreateDirectory 不是异步方法。只有FileStream 有异步方法。

      【讨论】:

      • 好的,我明白了。这是一个工作应用程序的实现,所以我试图在不重写方法结构的情况下实现这个功能。非常感谢。
      • 感谢您的解释。在尝试了许多不同的方法 2 天后,YIELD 正是我所需要的。
      猜你喜欢
      • 2022-07-01
      • 1970-01-01
      • 2020-11-20
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 2023-02-17
      • 1970-01-01
      • 2020-08-07
      相关资源
      最近更新 更多