Skip to content

Commit e67e21d

Browse files
committed
Cleanup to make client - worker relation more visible.
1 parent 5d47fb3 commit e67e21d

8 files changed

Lines changed: 70 additions & 81 deletions

File tree

README.md

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,46 +36,46 @@ Same as Linux but with `Win` suffix, e.g. `npm run integrateWin`, `buildWin:all:
3636

3737
## Communication
3838

39-
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 [index.js](react/src/index.js) and [main.js](react/src/main.js)) and executes them. These functions establish a Web Worker using the [dotnetWorker.js](dotnet/wwwroot/dotnetWorker.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.
39+
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.
4040

4141
From dotnet to react - exports ready.
4242

43-
+-------------------+ +--------------------------------------------------------+
44-
| React App | | WASM App +-----------------------+|
45-
| (Main Thread) | | (Main Thread) | Web Worker file ||
46-
|+-----------------+| | +------------------+ | (WebWorker Thread) ||
47-
|| QrImage || | | file with | | ||
48-
|| Component || | | imports to React | | ||
49-
|| || | | | | ||
50-
|| +-------------+ || | | +-------------+ | Message | +------------------+ ||
51-
|| | | || Event | | | | | Passing | | | ||
52-
|| | EventEmitter| || <------- | | | EventEmitter| | <------ | | JS | ||
53-
|| | on('READY') | || | | | emit() | | READY | | (starting dotnet)| ||
54-
|| | re-render | || | | | | | | | | ||
55-
|| | | || | | | | | | | | ||
56-
|| +-------------+ || | | +-------------+ | | +------------------+ ||
57-
|+-----------------+| | +------------------+ +-----------------------+|
58-
+-------------------+ +--------------------------------------------------------+
43+
+-------------------------------------------+ +---------------------+
44+
| React App | | WASM App |
45+
| (Main Thread) | | (WebWorker Thread) |
46+
|+---------------+ +---------------+| | +------------------+|
47+
|| QrImage | | client.js || | | worker.js ||
48+
|| Component | | || | | ||
49+
|| | | || | | ||
50+
||+-------------+| |+-------------+|| Message | | ||
51+
||| || Event || ||| Passing | | ||
52+
||| EventEmitter|| <------ || EventEmitter||| <------ | | startDotnet() ||
53+
||| on('READY') || || emit() ||| READY | | ||
54+
||| re-render || || ||| | | ||
55+
||| || || ||| | | ||
56+
||+-------------+| |+-------------+|| | | ||
57+
|+---------------+ +---------------+| | +------------------+|
58+
+-------------------------------------------+ +---------------------+
5959

6060
From react to dotnet - QR generation request.
6161

6262
From dotnet to react - populating frontend element with data.
6363

64-
+----------------------+ +--------------------------------------------------------------+
65-
| React App | | WASM App +-------------------------+|
66-
| (Main Thread) | | (Main Thread) | Web Worker file ||
67-
| +------------------+| | +------------------+ | (WebWorker Thread) ||
68-
| | QrImage || | | file with | | +--------------------+ ||
69-
| | Component || | | imports to React | | | C#'s exports | ||
70-
| | || Imported | | | Message | | (QR Generation) | ||
71-
| | +--------------+ || Function | | +-------------+ | Passing | +--------------------+ ||
72-
| | | Button | || Call | | | generate() | | -----------> | ||
73-
| | | onClick | || -------> | | | function | | | ^ Built-in | ||
74-
| | +--------------+ || | | +-------------+ | Message | | interop V ||
75-
| | +--------------+ || | | +-------------+ | Passing | ||
76-
| | | EventEmitter | || Event | | | EventEmitter| | Transferable | +--------------------+ ||
77-
| | | on('IMAGE') | || <------- | | | emit() | | <----------- | | JS's imports | ||
78-
| | | <img src=..> | || | | | | | | | | ||
79-
| | +--------------+ || | | +-------------+ | IMAGE | +--------------------+ ||
80-
| +------------------+| | +------------------+ +-------------------------+|
81-
+----------------------+ +--------------------------------------------------------------+
64+
+-----------------------------------------------+ +---------------------+
65+
| React App | | WASM App |
66+
| (Main Thread) | | (WebWorker Thread) |
67+
|+-----------------+ +----------------+| | +------------------+|
68+
|| QrImage | | client.js || | | C#'s exports ||
69+
|| Component | | || Message | | (QR Generation) ||
70+
|| | client | || Passing | | ||
71+
||+--------------+ | API | +-------------+|| -----------> | +------------------+|
72+
||| Button | | Call | | generate() ||| | |
73+
||| onClick | | -------> | | function ||| Message | ^ Built-in | |
74+
||+--------------+ | | +-------------+|| Passing | | interop V |
75+
||+--------------+ | | +-------------+|| Transferable | |
76+
||| EventEmitter | | Event | | EventEmitter||| <----------- | +------------------+|
77+
||| on('IMAGE') | | <------- | | emit() ||| IMAGE | | JS's imports ||
78+
||| <img src=..> | | | | ||| | | ||
79+
||+--------------+ | | +-------------+|| | | ||
80+
|+-----------------+ +----------------+| | +------------------+|
81+
+-----------------------------------------------+ +---------------------+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async function startDotnet(){
3737
assemblyExports = await getAssemblyExports(config.mainAssemblyName);
3838

3939
self.postMessage("setting imports");
40-
setModuleImports("dotnetWorker.js", {
40+
setModuleImports("worker.js", {
4141
QRGenerator: {
4242
sendErrorMessage
4343
}

react/src/App.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import { Popup } from './Popup.js';
66
import debounce from 'lodash.debounce';
77
import { QrImage } from './QRImage.js';
88
import React from 'react';
9-
import { getEventEmitter } from './index.js';
109
import { BarLoader } from 'react-spinners';
10+
import { eventEmitter } from './client.js';
1111

1212
function App() {
1313
const initText = "Type text to generate QR";
1414
const initSize = 5;
1515
const [text, setText] = useState(initText);
1616
const [size, setSize] = useState(initSize);
17-
const eventEmitter = getEventEmitter();
1817
const [exportsReady, setExportsReady] = useState(false);
1918
const debouncedSetText = debounce(e => setText(e.target.value), 100);
2019
const debouncedSetSize = debounce(e => setSize(e.target.value), 100);
@@ -24,7 +23,7 @@ function App() {
2423
setExportsReady(true);
2524
};
2625
eventEmitter.on('exportsReady', finishWaiting);
27-
}, [eventEmitter]);
26+
}, []);
2827

2928
return (
3029
<div className="App">

react/src/EventEmitter.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
export class EventEmitter {
4+
constructor() { this.events = {}; }
5+
6+
on(eventName, callback) {
7+
if (!this.events[eventName])
8+
this.events[eventName] = [];
9+
this.events[eventName].push(callback);
10+
}
11+
12+
emit(eventName, ...args) {
13+
if (this.events[eventName])
14+
this.events[eventName].forEach((callback) => callback(...args));
15+
}
16+
}

react/src/Popup.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
import React, { useState, useEffect } from 'react';
4-
import { getEventEmitter } from './index.js';
4+
import { eventEmitter } from './client';
55
import './Popup.css';
66

77
export const Popup = () => {
88
const [isOpen, setIsOpen] = useState(false);
99
const [popupMessage, setPopupMessage] = useState('');
10-
11-
const eventEmitter = getEventEmitter();
10+
1211
const togglePopup = () => {
1312
setIsOpen(!isOpen);
1413
};
@@ -23,7 +22,7 @@ export const Popup = () => {
2322
showPopup(message);
2423
};
2524
eventEmitter.on('errorOccurred', errorOccurredHandler);
26-
}, [eventEmitter]);
25+
}, []);
2726

2827
return (
2928
<div className="popup-container">

react/src/QRImage.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
import React, { useEffect, useState } from 'react'
4-
import { runGenerate } from './index.js';
5-
import { getEventEmitter } from './index.js';
4+
import { eventEmitter } from './client';
5+
import { generate } from './client';
66

77
export const QrImage = ({ text, size }) => {
88
const [imageUrl, setImageUrl] = useState(null);
9-
const eventEmitter = getEventEmitter();
109

1110
useEffect(() => {
1211
async function generateAsync() {
1312
if (text && size) {
14-
await runGenerate(text, size);
13+
await generate(text, size);
1514
}
1615
}
1716

@@ -27,7 +26,7 @@ export const QrImage = ({ text, size }) => {
2726
}
2827
};
2928
eventEmitter.on('generateQRCodeResponse', qrHandler);
30-
}, [eventEmitter]);
29+
}, []);
3130

3231
return (
3332
<div>
Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
3+
import { EventEmitter } from './EventEmitter.js';
4+
35
let dotnetWorker = null;
46
let exportsReady = false;
7+
export const eventEmitter = new EventEmitter();
58

6-
export async function setUpWorker() {
7-
dotnetWorker = new Worker('./qr/wwwroot/dotnetWorker.js', { type: "module" } );
8-
9+
export function setUpWorker() {
10+
dotnetWorker = new Worker('../../qr/wwwroot/worker.js', { type: "module" } );
911
dotnetWorker.addEventListener('message', function(e) {
1012
switch (e.data.command)
1113
{
@@ -45,21 +47,4 @@ export function generate(text, size) {
4547
throw new Error("Exports not ready yet, cannot generate QR code");
4648
}
4749
dotnetWorker.postMessage({ command: "generateQRCode", text: text, size: size });
48-
}
49-
50-
class EventEmitter {
51-
constructor() { this.events = {}; }
52-
53-
on(eventName, callback) {
54-
if (!this.events[eventName])
55-
this.events[eventName] = [];
56-
this.events[eventName].push(callback);
57-
}
58-
59-
emit(eventName, ...args) {
60-
if (this.events[eventName])
61-
this.events[eventName].forEach((callback) => callback(...args));
62-
}
63-
}
64-
65-
export const eventEmitter = new EventEmitter();
50+
}

react/src/index.js

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,14 @@ import React from 'react'
44
import ReactDOM from 'react-dom/client';
55
import './index.css';
66
import App from './App';
7+
import { setUpWorker, launchDotnet } from './client';
78

8-
const mainJsPath = '../../qr/wwwroot/main.js';
9-
const { setUpWorker, launchDotnet, generate, eventEmitter } = await import(/* webpackIgnore: true */mainJsPath);
10-
await setUpWorker();
9+
setUpWorker();
1110
launchDotnet();
1211

1312
const root = ReactDOM.createRoot(document.getElementById('root'));
1413
root.render(
1514
<React.StrictMode>
1615
<App />
1716
</React.StrictMode>
18-
);
19-
20-
export function runGenerate(text, size) {
21-
generate(text, size);
22-
}
23-
24-
export function getEventEmitter() {
25-
return eventEmitter;
26-
}
17+
);

0 commit comments

Comments
 (0)