Skip to content

[wasm] Request: Option to keep .NET main thread on browser UI thread when threading is enabled #126438

Description

@LostBeard

Description

When <WasmEnableThreads>true</WasmEnableThreads> is set, the .NET runtime moves entirely to a background Web Worker (the "Deputy Thread" model). This makes DotNet.invokeMethod — synchronous JS-to-.NET calls — impossible, because the browser's UI thread cannot block while waiting for a response from a worker.

I'm requesting a supported configuration option that enables .NET threading (thread pool, Task.Run, System.Threading) while keeping the main .NET entry point on the browser's UI thread.

Background

I maintain the SpawnDev.BlazorJS ecosystem — 41 NuGet packages, 323,000+ total downloads — which provides typed C# wrappers for JavaScript APIs in Blazor WebAssembly. This library depends on synchronous JS-to-.NET interop (DotNet.invokeMethod) because the JavaScript specification requires synchronous handling in many scenarios:

  • event.preventDefault() must be called synchronously within the handler
  • event.stopImmediatePropagation() — same constraint
  • beforeunload requires a synchronous return value
  • Synchronous property getters on JS objects
  • Callback-driven APIs that expect synchronous returns

These are not edge cases. These are fundamental browser APIs that cannot be handled asynchronously.

Proposed Behavior

A configuration like:

<PropertyGroup>
    <WasmEnableThreads>true</WasmEnableThreads>
    <WasmMainThreadOnUI>true</WasmMainThreadOnUI>
</PropertyGroup>

When enabled:

  • Program.Main executes on the browser's UI thread (as it does today without threading)
  • Synchronous JS interop (DotNet.invokeMethod, [JSExport] with sync signatures) works normally
  • Task.Run, ThreadPool, and new Thread() dispatch to background Web Workers
  • Blocking primitives (lock, Monitor.Enter, Thread.Sleep) work on background threads but throw on the UI thread (this is already the browser's constraint)

This is architecturally the same model that SpawnDev.BlazorJS.WebWorkers (90,000+ downloads) has used successfully for years: main .NET on the UI thread, heavy work dispatched to workers explicitly.

Why This Matters

Without this option, any .NET library that provides synchronous JavaScript interop is broken when threading is enabled. This affects:

  • SpawnDev.BlazorJS and its 41-package ecosystem (WebRTC, WebGPU, WebTorrent, Canvas, Crypto, etc.)
  • Any third-party library wrapping synchronous JS APIs
  • Any application handling DOM events that require synchronous responses

The Deputy Thread model is valuable for applications that prioritize background computation over DOM fidelity. But it should not be the only option. Developers who need synchronous JS interop should be able to opt into threading without losing it.

Related Issues

Environment

  • .NET 10
  • Blazor WebAssembly
  • All modern browsers (Chrome, Edge, Firefox, Safari)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions