Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kristofferstrube/blazor.webworkers
A Blazor wrapper for the Web Workers part of the HTML API.
https://github.com/kristofferstrube/blazor.webworkers
blazor multithreading wasm webworkers worker
Last synced: 3 months ago
JSON representation
A Blazor wrapper for the Web Workers part of the HTML API.
- Host: GitHub
- URL: https://github.com/kristofferstrube/blazor.webworkers
- Owner: KristofferStrube
- License: mit
- Created: 2024-06-18T22:02:49.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2024-10-02T20:56:07.000Z (4 months ago)
- Last Synced: 2024-11-07T11:03:00.599Z (3 months ago)
- Topics: blazor, multithreading, wasm, webworkers, worker
- Language: C#
- Homepage:
- Size: 7.54 MB
- Stars: 13
- Watchers: 1
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](/LICENSE)
[![GitHub issues](https://img.shields.io/github/issues/KristofferStrube/Blazor.WebWorkers)](https://github.com/KristofferStrube/Blazor.WebWorkers/issues)
[![GitHub forks](https://img.shields.io/github/forks/KristofferStrube/Blazor.WebWorkers)](https://github.com/KristofferStrube/Blazor.WebWorkers/network/members)
[![GitHub stars](https://img.shields.io/github/stars/KristofferStrube/Blazor.WebWorkers)](https://github.com/KristofferStrube/Blazor.WebWorkers/stargazers)# Blazor.WebWorkers
A Blazor wrapper for the [Web Workers part of the HTML API.](https://html.spec.whatwg.org/multipage/workers.html)
The API defines ways to run scripts in the background independently of the primary thread. This allows for long-running scripts that are not interrupted by scripts that respond to clicks or other user interactions and allows long tasks to be executed without yielding to keep the page responsive. This project implements a wrapper around the API for Blazor so that we can easily and safely create workers.**This wrapper is still just an experiment.**
# Demo
The sample project can be demoed at https://kristofferstrube.github.io/Blazor.WebWorkers/On each page, you can find the corresponding code for the example in the top right corner.
# Getting Started
Many others like [Tewr/BlazorWorker](https://github.com/Tewr/BlazorWorker) and [LostBeard/SpawnDev.BlazorJS](https://github.com/LostBeard/SpawnDev.BlazorJS) have made libraries like this before. This project differs a bit from the other projects by utilizing [the wasm-experimental workload](https://devblogs.microsoft.com/dotnet/use-net-7-from-any-javascript-app-in-net-7/). This simplifies the code needed for this to work a lot. The catch to this is that you will need to have the code for your workers in another project. For me this is not only a negative as it also makes it very clear that they do not share memory and that they run in separate contexts, similar to how the *Blazor WASM* project is separate in a *Blazor WebApp*.
So to get started you really only need to *create a new console project* and then make a few adjustments to the `.csproj`. In the end it should look something like this:
```xml
net8.0
Exe
browser-wasm
enable
enable
true
```
And then you can do whatever you want in the `Program.cs` file, but I've added some helpers that make it easier to communicate with the main window and create objects.## SlimWorker
Here I have an example of the code needed for a simple pong worker that broadcasts when it is ready to listen for a ping, responds with a pong when it receives that, and then shuts down.
```csharp
using KristofferStrube.Blazor.WebWorkers;if (!OperatingSystem.IsBrowser())
throw new PlatformNotSupportedException("Can only be run in the browser!");Console.WriteLine("Hey this is running on another thread!");
bool keepRunning = true;
if (args.Length >= 1)
{
Console.WriteLine($"The worker was initialized with arguments: [{string.Join(", ", args)}]");
}// This is a helper for listening on messages.
Imports.RegisterOnMessage(e =>
{
// If we receive a "ping", respond with "pong".
if (e.GetTypeOfProperty("data") == "string" && e.GetPropertyAsString("data") == "ping")
{
// Helper for posting a message.
Console.WriteLine("Received ping; Sending pong!");
Imports.PostMessage("pong");
keepRunning = false;
}
});Console.WriteLine("We are now listening for messages.");
Imports.PostMessage("ready");// We run forever to keep it alive.
while (keepRunning)
await Task.Delay(100);Console.WriteLine("Worker done, so stopping!");
```And with very little extra setup we can start this and post a message to it once it is ready. For this we use a very simple abstraction over the worker that enable us to specify an assembly name and some arguments for the worker.
```csharp
SlimWorker slimWorker = await SlimWorker.CreateAsync(
jSRuntime: JSRuntime,
assembly: typeof(AssemblyPongWorker).Assembly.GetName().Name!,
["Argument1", "Argument2"]
);EventListener eventListener = default!;
eventListener = await EventListener.CreateAsync(JSRuntime, async e =>
{
object? data = await e.Data.GetValueAsync();
switch (data)
{
case "ready":
Log("We are sending a ping!");
await slimWorker.PostMessageAsync("ping");
break;
case "pong":
Log("We received a pong!");
await slimWorker.RemoveOnMessageEventListenerAsync(eventListener);
await eventListener.DisposeAsync();
await slimWorker.DisposeAsync();
break;
}
});
await slimWorker.AddOnMessageEventListenerAsync(eventListener);
```
This looks like so:![ping pong demo](./docs/ping-pong.gif?raw=true)
## JobWorker
Another more basic abstraction is the `JobWorker`. This simple abstraction runs some job with an input and an output on a worker. The `.csproj` look identical to the one used for the `SlimWorker`.
But what differs is that we need to create a class that implements the interface `IJob` in the worker project. A simple way to do this is by extending the abstract class `JsonJob` which uses JSON as the format for transfering its input and output. This limits us to only use inputs and outputs that can be JSON serialized and deserialized.
Here were implement a job that can find the sum of the codes of each individual char in a string.
```csharp
public class StringSumJob : JsonJob
{
public override int Work(string input)
{
int result = 0;
for (int i = 0; i < input.Length; i++)
{
result += input[i];
}
return result;
}
}
```Then we need to replace the content of the `Program.cs` in the worker project with the following to instantiate the job.
```csharp
if (!OperatingSystem.IsBrowser())
throw new PlatformNotSupportedException("Can only be run in the browser!");new StringSumJob().Execute(args);
```Finally to call the worker from our main Blazor program we only need the following.
```csharp
var jobWorker = await JobWorker.CreateAsync(JSRuntime);int result = await jobWorker.ExecuteAsync(input);
```We can create the `JobWorker` a single time and then run it multiple times with different inputs. Doing this spares us from importing the needed WASM assemblies multiple times which can make consecutive runs much faster.
# Related repositories
The library uses the following other packages to support its features:
- https://github.com/KristofferStrube/Blazor.WebIDL (To make error handling JSInterop)
- https://github.com/KristofferStrube/Blazor.DOM (To implement *EventTarget*'s in the package like `Worker`)
- https://github.com/KristofferStrube/Blazor.Window (To use the `MessageEvent` type)# Related articles
This repository was built with inspiration and help from the following series of articles:- [Multithreading in Blazor WASM using Web Workers](https://kristoffer-strube.dk/post/multithreading-in-blazor-wasm-using-web-workers/)
- [Use .NET from any JavaScript app in .NET 7](https://devblogs.microsoft.com/dotnet/use-net-7-from-any-javascript-app-in-net-7/)
- [Typed exceptions for JSInterop in Blazor](https://kristoffer-strube.dk/post/typed-exceptions-for-jsinterop-in-blazor/)
- [Blazor WASM 404 error and fix for GitHub Pages](https://blog.elmah.io/blazor-wasm-404-error-and-fix-for-github-pages/)
- [How to fix Blazor WASM base path problems](https://blog.elmah.io/how-to-fix-blazor-wasm-base-path-problems/)