1

I'm currently trying to call asynchronously an expensive method in blazor WASM while not blocking the UI thread.

According to answers to this question, it is impossible. However, these date back to a couple of years ago, and according to the WebAssembly roadmap, threading is supported by modern browsers.

Also, I can await function calls without blocking the UI thread, as long as the functions are awaitable from the bottom up.

In the following code snippet, clicking the Foo button will cause the UI to hang for a couple of seconds while the operation is carried out, but clicking the Bar button will process a similar (but awaitable) operation in the background while keeping the UI responsive.

If WebAssembly doesn't support multi-threading, why does the second option seem to work with multiple threads? If it does support multi-threading, why does the first option block the UI?

@page "/asynctest"

<div class="row">
    <div class="col">
        @DisplayValue
    </div>
    <div class="col">
        <button type="button" @onclick="OnClickFoo">Foo</button>
    </div> 
    <div class="col">
        <button type="button" @onclick="OnClickBar">Bar</button>
    </div> 
</div>

@code{
    private string DisplayValue = "";
    private double Result;
    private async Task OnClickFoo(EventArgs e)
    {
        DisplayValue = "Working...";
        Result = await Task.Run(() => Frobnicate()); // Blocks UI thread
        DisplayValue = Result.ToString();
    }
    private async Task OnClickBar(EventArgs e)
    {
        DisplayValue = "Working...";
        Result = await FrobnicateAsync(); // Doesn't block UI thread
        DisplayValue = Result.ToString();
    }
    private int Frobnicate()
    {
        Thread.Sleep(2000); // do some heavy work
        return 42;
    }
    private async Task<int> FrobnicateAsync()
    {
        await Task.Delay(2000); // do some heavy work async
        return 42;
    }
}
gazoh
  • 237
  • 3
  • 11
  • 1
    @Fildor I tried, it doesn't make any difference on the UI blocking, the only effect is that the UI isn't updated upon completion, requiring to call `StateHasChanged()` manually. – gazoh Mar 29 '22 at 15:19
  • Try this: `Result = await Task.Run(async () => { await Task.Delay(1); return Frobnicate(); });` – Rand Random Mar 29 '22 at 15:28
  • 3
    @Fildor - do **not** use async void in Blazor. You don't need them, you don't want them. – H H Mar 29 '22 at 16:01
  • @HenkHolterman OK. Is that a "speciality" of Blazor? Wasn't aware of that. – Fildor Mar 30 '22 at 08:51
  • Yes, more or less. Blazor Events will adapt to a Task return type and await it. – H H Mar 30 '22 at 09:08

1 Answers1

6

As far as I am aware, Blazor WASM does not support multithreading, you only get 1 thread.

In the "OnClickBar" method, you are not creating a different thread. You are using the async/await state machine which returns control to the calling thread. The Task.Delay(2000) does not block the thread as seen in this Stack Overflow question

In the "OnClickFoo" method, you are calling Task.Run which does try to create a background thread - this does not work properly in WASM as you have a single thread, so when you call Thread.Sleep(2000); - it is the UI thread that is sleeping.

More info on threading here: Microsoft Docs - Threads

Related Stack Overflow question

  • 3
    This is correct. Multi-Threading WASM is on the roadmap, but when ...????. `Thread.Sleep` is blocking and blocks the only thread available. – MrC aka Shaun Curtis Mar 29 '22 at 15:39
  • 3
    Exactly, threads are supposed to be introduced in .net 7, but it is not yet in the current preview https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-2/. Also See this thread https://github.com/dotnet/aspnetcore/issues/17730 – Shuryno Mar 29 '22 at 15:45