为什么在Blazor wasm中使用await Task.Delay(1) ?



许多SO答案使用await Task.Delay(1)来解决Blazor (wasm)中的各种异步渲染问题。我甚至在我自己的代码中发现了一些地方这样做"使它工作"。

然而,它总是被当作事实来陈述,没有一个彻底的解释,而且我在文档中也找不到这种技术。

一些问题:

  • 为什么使用await Task.Delay(1)-我什么时候使用这种技术,用例是什么?
  • 文档没有讨论这个(我可以找到);是因为它是一种hack,还是因为它是处理用例的合法方法?
  • Task.Delay(1)Task.Yield()有区别吗?

为什么使用await Task.Delay(1) -我什么时候使用这种技术,用例是什么?

它让UI有机会在你的代码中更新和重新绘制。

文档没有讨论这个(我可以找到);是因为它是一种hack,还是因为它是处理用例的合法方法?

这是一个hack。但在Blazor/WASM的特定情况下(没有其他Blazor或。net运行时),没有很多其他选择。如果可能的话,我建议拆分你的逻辑,这样你的应用就不会一次做那么多事情;但有时这是不可能的(或不容易)。

Task.Delay(1)和Task.Yield()有什么区别?

根据浏览器的具体情况,可以。

在Windows UI应用程序中,Task.Yield不会为此工作,因为UI消息循环是一个优先队列,并且"运行此代码";是最高优先级。因此(再次,对于Windows UI应用程序),这将使方法的其余部分排队,然后返回到消息循环,然后继续执行代码,而不是刷新UI(这是低优先级的消息)。

对于Blazor/WASM,Task.Yield是否工作取决于浏览器对其(隐式)消息循环的实现。如果它有类似的优先级队列,那么你最终会遇到与Windows UI相同的问题,其中Task.Yield确实屈服于消息循环,但没有排出

在所有平台上,Task.Delay(1)实际上排队一个定时器回调,这通常是足够的时间来处理一些UI更新之前,代码继续运行。

  • 为什么使用await Task.Delay(1)

显示事件处理程序中的中间结果。

  • 文档没有讨论这个

通常不需要。但也没有理由反对使用它。我知道如何用它来解决这样的问题。我得到了一些负面的反馈,看看那些帖子下面的评论。

  • Task.Delay(1)和Task.Yield()有什么区别?

是的,Task.Yield()看起来更明智,但我发现它并不总是工作。请看Stephen Cleary的回答。

下面是我和Henk Holterman回答的一个问题的链接,他在回答中使用了await Task.Delay(1);

运行代码,并查看差异,例如,使用await Task.Delay(1);会导致重新呈现组件两次,等等。

使用await Task.Delay(1);是必要的吗?绝对不是。这是一种糟糕的做法,不仅会导致组件的第二次重新呈现,而且还可能导致复杂代码的微妙问题。Blazor提供了一个生命周期方法列表,您可以捕获并使用这些方法来提供所需的解决方案。请不要入侵。从长远来看,这可能是非常昂贵的。创建优雅的代码,而不是黑客…

更新下面的代码片段描述了使用Task.Delay,演示带有标题为"Save,"按钮元素的页面的用例。要求是将标题的文本更改为"Saving…,"在用户单击该按钮后立即,在将员工记录保存到数据存储中的持续时间内。如果你知道Task.Delay,的其他用例,请告诉我。

Index.razor

@page "/"
<div>
<button class="btn btn-primary" 
@onclick="Save">@caption</button>
</div>

@code 
{

private string caption = "Save";
private async Task SaveEmployee()
{
// Simulate saving an employee's record in database...
// I use System.Threading.Thread.Sleep instead of a loop 
// with millions of iterations.
System.Threading.Thread.Sleep(3000);
// Retruns completed task
await Task.CompletedTask;
}
private async Task Save()
{
caption = "Saving...";
// Renders here automatically after calling Task.Delay()

await Task.Delay(1000);
await SaveEmployee();
caption = "Save";
// Renders here automatically when SaveEmployee() 
//complete
}
}

另一方面,下面的代码片段演示了如何不使用Task.Delay,,并提供了一个优雅的解决方案,这当然不是一个hack,其额外的优点是它导致组件的单一呈现…Task.Delay涉及第二次渲染,请注意…

注意:下面的代码是这个问题的答案

Component.razor

<div @ref="ReferenceToDiv" id="select-@Id" style="background-color: red; width:300px; height: 300px">
</div>
@code
{
ElementReference ReferenceToDiv;
// As you can see, you should call the "adjustPosition" method from the 
// `OnAfterRenderAsync` method to ensure that the div element has been 
// rendered. DO Not Re-render In Vain. That is, do not use
// await Task.Delay(1); to re-render your component
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (opened)
{
await jsModule.InvokeVoidAsync("adjustPosition", ReferenceToDiv);                
} 
}
public void OnClick()
{
opened = !opened;

}
}

. js

export function adjustPosition(element) {
// Should return 300px
console.log($(element.style.width);   
}

最新更新