Skip to content

Commit

Permalink
Simplify messaging.
Browse files Browse the repository at this point in the history
  • Loading branch information
ilonatommy committed May 21, 2024
1 parent 2e07d25 commit 782253d
Show file tree
Hide file tree
Showing 11 changed files with 3,897 additions and 17,120 deletions.
29 changes: 5 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,6 @@ Same as Linux but with `Win` suffix, e.g. `npm run integrateWin`, `buildWin:all:

The React application runs in the main thread, that has access to DOM. It imports functions for launching a .NET runtime on a Web Worker from a WebAssembly (WASM) application (refer to [client.js](react/src/client.js)) and executes them. These functions establish a Web Worker using the [worker.js](dotnet/wwwroot/worker.js) file. Web Worker can perform heavy tasks without blocking the UI, however it does not have direct control over DOM and relies on communication with main thread for changes to UI. Communication between the Web Worker and the main thread occurs through message passing. The demo includes a few simple examples of passing information from dotnet to React frontend and the other way.

From dotnet to react - exports ready.

+-------------------------------------------+ +---------------------+
| React App | | WASM App |
| (Main Thread) | | (WebWorker Thread) |
|+---------------+ +---------------+| | +------------------+|
|| QrImage | | client.js || | | worker.js ||
|| Component | | || | | ||
|| | | || | | ||
||+-------------+| |+-------------+|| Message | | ||
||| || Event || ||| Passing | | ||
||| EventEmitter|| <------ || EventEmitter||| <------ | | startDotnet() ||
||| on('READY') || || emit() ||| READY | | ||
||| re-render || || ||| | | ||
||| || || ||| | | ||
||+-------------+| |+-------------+|| | | ||
|+---------------+ +---------------+| | +------------------+|
+-------------------------------------------+ +---------------------+

From react to dotnet - QR generation request.

From dotnet to react - populating frontend element with data.
Expand All @@ -71,11 +52,11 @@ From dotnet to react - populating frontend element with data.
||+--------------+ | API | +-------------+|| -----------> | +------------------+|
||| Button | | Call | | generate() ||| | |
||| onClick | | -------> | | function ||| Message | ^ Built-in | |
||+--------------+ | | +-------------+|| Passing | | interop V |
||+--------------+ | | +-------------+|| Transferable | |
||| EventEmitter | | Event | | EventEmitter||| <----------- | +------------------+|
||| on('IMAGE') | | <------- | | emit() ||| IMAGE | | JS's imports ||
||| <img src=..> | | | | ||| | | ||
||+--------------+ | | | ||| Passing | | interop V |
||+--------------+ | | | ||| Transferable | |
||| | | update | | ||| <----------- | +------------------+|
||| <img src=..> | | <------- | | ||| IMAGE | | JS's imports ||
||| | | | | ||| | | ||
||+--------------+ | | +-------------+|| | | ||
|+-----------------+ +----------------+| | +------------------+|
+-----------------------------------------------+ +---------------------+
7 changes: 2 additions & 5 deletions dotnet/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices.JavaScript;
using QRCoder;
Expand All @@ -18,15 +19,11 @@ internal static byte[] Generate(string text, int qrSize)
{
if (qrSize >= MAX_QR_SIZE)
{
SendErrorMessage($"QR code size must be less than {MAX_QR_SIZE}. Try again.");
return Array.Empty<byte>();
throw new Exception($"QR code size must be less than {MAX_QR_SIZE}. Try again.");
}
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(text, QRCodeGenerator.ECCLevel.Q);
BitmapByteQRCode qrCode = new BitmapByteQRCode(qrCodeData);
return qrCode.GetGraphic(qrSize);
}

[JSImport("QRGenerator.sendErrorMessage", "dotnetWorker.js")]
internal static partial void SendErrorMessage(string message);
}
85 changes: 37 additions & 48 deletions dotnet/wwwroot/worker.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { dotnet, exit } from './_framework/dotnet.js'

import { dotnet } from './_framework/dotnet.js'

let assemblyExports = null;
let startupError = undefined;

self.addEventListener('message', async function(e) {
switch (e.data.command)
{
case "startDotnet":
console.log("Starting dotnet, says the worker");
await startDotnet();
break;
case "generateQRCode":
if (!assemblyExports)
throw new Error("Exports not found");
const size = Number(e.data.size);
const text = e.data.text;
if (size === undefined || text === undefined)
new Error("Inner error, got empty QR generation data from React");
const imageBytes = assemblyExports.QRGenerator.Generate(text, size);
self.postMessage({ command: "generateQRCodeResponse", image: imageBytes.buffer }, [imageBytes.buffer]);
default:
self.postMessage(e.data.command);
break;
}
}, false);
try {
const { getAssemblyExports, getConfig } = await dotnet.create();
const config = getConfig();
assemblyExports = await getAssemblyExports(config.mainAssemblyName);
}
catch (err) {
startupError = err.message;
}

async function startDotnet(){
self.addEventListener('message', async function(e) {
try {
self.postMessage("creating dotnet");
const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.create();
self.postMessage("getting config");
const config = getConfig();
self.postMessage("getting exports");
assemblyExports = await getAssemblyExports(config.mainAssemblyName);

self.postMessage("setting imports");
setModuleImports("worker.js", {
QRGenerator: {
sendErrorMessage
}
if (!assemblyExports) {
throw new Error(startupError || "worker exports not loaded");
}
let result = null;
switch (e.data.command) {
case "generateQR":
const size = Number(e.data.size);
const text = e.data.text;
if (size === undefined || text === undefined)
new Error("Inner error, got empty QR generation data from React");
result = assemblyExports.QRGenerator.Generate(text, size);
break;
default:
throw new Error("Unknown command: " + e.data.command);
}
self.postMessage({
command: "response",
requestId: e.data.requestId,
result,
});

self.postMessage({ command: "exportsReady" });
self.postMessage("starting dotnet");
await dotnet.run();
}
catch (err) {
console.log(`err: ${err}; ${err.message}`);
exit(2, err);
self.postMessage({
command: "response",
requestId: e.data.requestId,
error: err.message,
});
}
}

export function sendErrorMessage(message) {
console.log(`sendErrorMessage is passing message: ${message}`);
self.postMessage({ command: "error", message: message });
}
}, false);
Loading

0 comments on commit 782253d

Please sign in to comment.