Reputation: 20489
Many SO answers use await Task.Delay(1)
to solve various async rendering issues in Blazor (wasm). I've even found a number of places in my own code where doing that "makes it work".
However it's always stated as matter of fact, without a thorough explanation, and I can't find this technique in the docs either.
Some questions:
await Task.Delay(1)
- when would I use this technique, what is the use case?Task.Delay(1)
and Task.Yield()
?Upvotes: 6
Views: 3134
Reputation: 273169
- Why use await Task.Delay(1)
To show intermediate results in an eventhandler. The canonical code for a screen update is
StateHasChanged(); // request a render
await Task.Delay(1); // allow the render to execute
- The docs do not discuss this
It is usually not needed. But there's no argument against using it either. I figured out how to use it when solving a problem like this. And I got some negative feedback, see the comments under those posts.
- Any difference between Task.Delay(1) and Task.Yield()?
Yes, Task.Yield() looks more sensible but I found it does not always work. See Stephen Cleary's answer here.
Upvotes: 3
Reputation: 45586
Below is a link to a question answered by me and by Henk Holterman
, in whose answer he uses await Task.Delay(1);
Run the code, and see the difference, as for instance, using await Task.Delay(1);
results in re-rendering the component twice, etc.
Is the use of await Task.Delay(1);
necessary?
Absolutely not. This is a bad practice that not only results in a second re-rendering of a component, but it may lead to subtle issues with complex code. Blazor offers a list of life-cycle methods which you can capture and use to provide the wanted solutions. No hacking, please. This may prove very expensive in the long run. Create elegant code, not hacking...
The code snippet below describes a use case for the use of Task.Delay,
demonstrating a page with a button element with the caption "Save," The requirement is to alter the caption's text to "Saving...," immediately after the user clicks the button, for the duration of saving an employee record in a data store. If you know of other use cases for Task.Delay,
please let me know.
@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
}
}
On the other hand, the following code snipppet demonstrates how to not use Task.Delay,
and provides an elegant solution, which is certainly not an hack, and whose additional advantage is that it results in a single rendering of the component... Task.Delay
involve a second rendering, mind you...
Note: The code below is an answer to this question
<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;
}
}
export function adjustPosition(element) {
// Should return 300px
console.log($(element.style.width);
}
Upvotes: 0
Reputation: 456322
Why use await Task.Delay(1) - when would I use this technique, what is the use case?
It gives the UI a chance to update and redraw in the middle of your code.
The docs do not discuss this (that I could find); is it because it's a hack, or is it a legitimate way to deal with the use case?
It's a hack. But in the specific case of Blazor/WASM (no other Blazor or .NET runtime), there's not a lot of other options. If possible, I'd suggest splitting up your logic so your app isn't doing so much all at once; but sometimes that's not possible (or easy).
Any difference between Task.Delay(1) and Task.Yield()?
Depending on the browser details, yes.
On Windows UI apps, Task.Yield
won't work for this because the UI message loop is a priority queue and "run this code" is highest priority. So (again, for Windows UI apps), this would queue the rest of the method and then return to the message loop, which would then continue executing the code instead of refreshing the UI (which are lower-priority messages).
For Blazor/WASM, whether Task.Yield
would work or not depends on the browser implementation of its (implicit) message loop. If it has a similar priority queue, then you'd end up with the same problem as a Windows UI where Task.Yield
does yield to the message loop but doesn't drain it.
On all platforms, Task.Delay(1)
actually queues a timer callback, which is generally enough time to get some UI updates handled before the code continues running.
Upvotes: 8