Skip to content

Commit 1eac8dd

Browse files
authored
Merge pull request #36969 from dotnet/main
Merge to Live
2 parents c8b3a51 + 9fb9a14 commit 1eac8dd

File tree

3 files changed

+242
-37
lines changed

3 files changed

+242
-37
lines changed

aspnetcore/blazor/blazor-with-dotnet-on-web-workers.md

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: Learn how to use Web Workers to enable JavaScript to run on separat
66
monikerRange: '>= aspnetcore-8.0'
77
ms.author: wpickett
88
ms.custom: mvc
9-
ms.date: 03/13/2026
9+
ms.date: 04/07/2026
1010
uid: blazor/blazor-web-workers
1111
---
1212
# ASP.NET Core Blazor with .NET on Web Workers
@@ -21,18 +21,18 @@ Modern Blazor WebAssembly apps often handle CPU-intensive work alongside rich UI
2121

2222
:::moniker range=">= aspnetcore-11.0"
2323

24-
The `webworker` project template provides built-in scaffolding for running .NET code in a Web Worker. The template generates the required JavaScript worker scripts and a C# `WebWorkerClient` class, which removes the need to write the interop layer manually. To learn about Web Workers with React, see <xref:client-side/dotnet-on-webworkers>.
24+
The Blazor Web Worker project template (`dotnet new blazorwebworker`) provides built-in scaffolding for running .NET code in a Web Worker in a Blazor WebAssembly app. The template generates the required JavaScript worker scripts, a C# `WebWorkerClient` class, and a starter `WorkerMethods.cs` file, which removes the need to write the interop layer manually. To learn about Web Workers with React, see <xref:client-side/dotnet-on-webworkers>.
2525

2626
> [!NOTE]
27-
> The `webworker` template isn't limited to Blazor. The template works with any .NET WebAssembly host, including standalone `wasmbrowser` apps and custom JavaScript frontends, such as React or vanilla JS. In non-Blazor scenarios, import the template's JavaScript client (`dotnet-web-worker-client.js`) directly from your entry point and call `[JSExport]` methods without the Blazor-specific C# `WebWorkerClient` class.
27+
> In .NET 11 and later, the Blazor Web Worker template (`blazorwebworker`) is intended for Blazor WebAssembly scenarios. For React or other custom JavaScript frontends, use the manual approach in <xref:client-side/dotnet-on-webworkers>.
2828
2929
## Create the projects
3030

3131
Create a Blazor WebAssembly app and a .NET Web Worker class library:
3232

3333
```dotnetcli
3434
dotnet new blazorwasm -n SampleApp
35-
dotnet new webworker -n WebWorker
35+
dotnet new blazorwebworker -n WebWorker
3636
```
3737

3838
Add a project reference from the app to the worker library:
@@ -42,34 +42,25 @@ cd SampleApp
4242
dotnet add reference ../WebWorker/WebWorker.csproj
4343
```
4444

45-
## Enable `AllowUnsafeBlocks`
45+
## Customize the generated worker methods
4646

47-
Enable the <xref:Microsoft.Build.Tasks.Csc.AllowUnsafeBlocks> property in the app's project file (`SampleApp.csproj`), which is required for [`[JSExport]` attribute](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) usage:
47+
The template already enables the <xref:Microsoft.Build.Tasks.Csc.AllowUnsafeBlocks> property in the worker project and includes a starter `WorkerMethods.cs` file with a [`[JSExport]` attribute](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) example. Add or update methods in the worker project as needed.
4848

49-
```xml
50-
<PropertyGroup>
51-
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
52-
</PropertyGroup>
53-
```
54-
55-
> [!WARNING]
56-
> The JS interop API requires enabling <xref:Microsoft.Build.Tasks.Csc.AllowUnsafeBlocks>. Be careful when implementing your own unsafe code in .NET apps, which can introduce security and stability risks. For more information, see [Unsafe code, pointer types, and function pointers](/dotnet/csharp/language-reference/unsafe-code).
49+
Worker methods are `static` methods marked with [`[JSExport]`](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) in a `static partial class`.
5750

58-
## Define worker methods
51+
Due to `[JSExport]` limitations, worker methods should use JS interop-friendly types such as primitives and strings. For complex data, serialize to JSON in the worker and deserialize it in the Blazor app.
5952

60-
Worker methods are `static` methods marked with [`[JSExport]`](xref:System.Runtime.InteropServices.JavaScript.JSExportAttribute) in a `static partial class`. Define them in the main application project because the assembly name must match the one used by the worker runtime.
61-
62-
Due to `[JSExport]` limitations, worker methods can only return primitives or strings. For complex types, serialize to JSON before returning. The `WebWorkerClient` automatically deserializes JSON results.
63-
64-
`Worker.cs`:
53+
`WebWorker\WorkerMethods.cs`:
6554

6655
```csharp
6756
using System.Runtime.InteropServices.JavaScript;
6857
using System.Runtime.Versioning;
6958
using System.Text.Json;
7059

60+
namespace WebWorker;
61+
7162
[SupportedOSPlatform("browser")]
72-
public static partial class Worker
63+
public static partial class WorkerMethods
7364
{
7465
[JSExport]
7566
public static string Greet(string name) => $"Hello, {name}!";
@@ -124,6 +115,7 @@ Inject `IJSRuntime` and use `WebWorkerClient.CreateAsync` to create a worker ins
124115
`Pages/Home.razor.cs`:
125116

126117
```csharp
118+
using System.Text.Json;
127119
using System.Runtime.Versioning;
128120
using Microsoft.AspNetCore.Components;
129121
using Microsoft.JSInterop;
@@ -155,10 +147,12 @@ public partial class Home : ComponentBase, IAsyncDisposable
155147
}
156148

157149
greeting = await worker.InvokeAsync<string>(
158-
"SampleApp.Worker.Greet", ["World"]);
150+
"WebWorker.WorkerMethods.Greet", ["World"]);
159151

160-
users = await worker.InvokeAsync<List<User>>(
161-
"SampleApp.Worker.GetUsers", []);
152+
var usersJson = await worker.InvokeAsync<string>(
153+
"WebWorker.WorkerMethods.GetUsers", []);
154+
155+
users = JsonSerializer.Deserialize<List<User>>(usersJson) ?? [];
162156
}
163157

164158
public async ValueTask DisposeAsync()
@@ -173,18 +167,20 @@ public partial class Home : ComponentBase, IAsyncDisposable
173167

174168
## Template output
175169

176-
The `dotnet new webworker` template generates a class library with the following structure:
170+
The `dotnet new blazorwebworker` template generates a class library with the following structure:
177171

178172
```
179173
WebWorker/
180174
├── WebWorker.csproj
181175
├── WebWorkerClient.cs
176+
├── WorkerMethods.cs
182177
└── wwwroot/
183178
├── dotnet-web-worker-client.js
184179
└── dotnet-web-worker.js
185180
```
186181

187182
* `WebWorkerClient.cs`: C# client that manages worker lifecycle and communication.
183+
* `WorkerMethods.cs`: Starter file for adding `[JSExport]` methods that run inside the worker.
188184
* `dotnet-web-worker-client.js`: JavaScript class that creates the worker, dispatches messages, and resolves pending requests.
189185
* `dotnet-web-worker.js`: Worker entry point that boots the .NET WebAssembly runtime and dynamically resolves `[JSExport]` methods by name.
190186

@@ -196,18 +192,30 @@ The `WebWorkerClient` class exposes an async API for communicating with a Web Wo
196192
public sealed class WebWorkerClient : IAsyncDisposable
197193
{
198194
public static async Task<WebWorkerClient> CreateAsync(
199-
IJSRuntime jsRuntime);
195+
IJSRuntime jsRuntime,
196+
int timeoutMs = 60000,
197+
string? assemblyName = null,
198+
CancellationToken cancellationToken = default);
200199

201200
public async Task<TResult> InvokeAsync<TResult>(
202-
string method, object[] args,
201+
string method,
202+
object[] args,
203+
int timeoutMs = 60000,
204+
CancellationToken cancellationToken = default);
205+
206+
public async Task InvokeVoidAsync(
207+
string method,
208+
object[] args,
209+
int timeoutMs = 60000,
203210
CancellationToken cancellationToken = default);
204211

205212
public async ValueTask DisposeAsync();
206213
}
207214
```
208215

209-
* `CreateAsync`: Initializes the worker and waits for the .NET runtime to be ready inside the worker thread.
210-
* `InvokeAsync<TResult>`: Calls a `[JSExport]` method on the worker by its full name (`Namespace.ClassName.MethodName`) and returns the deserialized result. JSON string results are automatically parsed into `TResult`.
216+
* `CreateAsync`: Initializes the worker and waits for the .NET runtime to be ready inside the worker thread. The `assemblyName` parameter defaults to the worker project's assembly.
217+
* `InvokeAsync<TResult>`: Calls a `[JSExport]` method on the worker by its full name (`AssemblyName.ClassName.MethodName`) and returns the result.
218+
* `InvokeVoidAsync`: Calls a `[JSExport]` method that doesn't return a value.
211219
* `DisposeAsync`: Terminates the worker and releases resources. Use `await using` or call explicitly.
212220

213221
:::moniker-end

0 commit comments

Comments
 (0)