Skip to content

Commit 772a48b

Browse files
authored
Merge pull request #4 from duart38/async_handler
Async handler support
2 parents 2682042 + 7b5cb65 commit 772a48b

File tree

11 files changed

+144
-75
lines changed

11 files changed

+144
-75
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
3. Allows you to Thread already existing functions
1414
5. Allows module imports inside the worker
1515

16-
## Example
16+
## Examples
17+
> See examples folder for more examples
18+
1719
```typescript
1820
let thread = new Thread<number>((e: MessageEvent)=>{
1921
console.log('Worker: Message received from main script');
@@ -44,6 +46,22 @@ new Thread(someFunction, "module"); // thread an already existing function
4446
new Thread(someFunction, "module", ['import Something from "../some.bundle.js";']); // thread with custom importing
4547
```
4648

49+
**Async support**
50+
```TypeScript
51+
const thread = new Thread<string, number>(async (_) => {
52+
console.log("Worker: Message received from main script");
53+
// Some async logic...
54+
await new Promise((ir) => setTimeout(ir, 2000));
55+
return "DONE";
56+
}, "module");
57+
58+
thread.onMessage((e) => {
59+
console.log(`recived back from thread: ${e}`);
60+
});
61+
62+
thread.postMessage(0);
63+
```
64+
4765
## API
4866

4967
### Standard API

Thread.bundle.js

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1+
// deno-fmt-ignore-file
2+
// deno-lint-ignore-file
3+
// This code was bundled using `deno bundle` and it's not recommended to edit it manually
4+
15
class Thread {
26
worker;
37
imports;
48
blob;
59
blobURL = "";
610
stopped = false;
7-
constructor(operation, type1, imports){
11+
constructor(operation, type, imports){
812
imports?.forEach((v)=>{
913
if (v.endsWith(".ts'") || v.endsWith('.ts"')) {
1014
throw new Error("Threaded imports do no support typescript files");
1115
}
1216
});
1317
this.imports = imports || [];
1418
this.blob = this.populateFile(operation);
15-
this.blob.then(async (b)=>console.log(await b.text())
16-
);
17-
this.worker = this.makeWorker(type1);
19+
this.worker = this.makeWorker(type);
1820
}
1921
async makeWorker(type) {
2022
this.blobURL = URL.createObjectURL(await this.blob);
@@ -23,39 +25,45 @@ class Thread {
2325
});
2426
}
2527
async populateFile(code) {
26-
let imported = this.imports?.flatMap(async (val)=>(await this.copyDep(val)).join("\n")
27-
);
28+
const imported = this.imports?.flatMap(async (val)=>(await this.copyDep(val)).join("\n"));
2829
return new Blob([
29-
`\n ${(await Promise.all(imported)).join("\n")}\n \n var global = {};\n var userCode = ${code.toString()}\n \n onmessage = function(e) {\n postMessage(userCode(e, global));\n }\n \n `
30+
`
31+
${(await Promise.all(imported)).join("\n")}
32+
33+
var global = {};
34+
var userCode = ${code.toString()}
35+
36+
onmessage = async function(e) {
37+
postMessage(await userCode(e, global));
38+
}
39+
40+
`
3041
]);
3142
}
3243
async copyDep(str) {
33-
var importPathRegex = /('|"|`)(.+\.js)(\1)/ig;
34-
var importInsRegex = /(import( |))({.+}|.+)(from( |))/ig;
35-
var matchedPath = importPathRegex.exec(str) || "";
36-
var file = false;
37-
var fqfn = "";
44+
const importPathRegex = /('|"|`)(.+\.js)(\1)/ig;
45+
const importInsRegex = /(import( |))({.+}|.+)(from( |))/ig;
46+
const matchedPath = importPathRegex.exec(str) || "";
47+
let file = false;
48+
let fqfn = "";
3849
if (!matchedPath[0].includes("http://") && !matchedPath[0].includes("https://")) {
3950
file = true;
4051
fqfn = matchedPath[0].replaceAll(/('|"|`)/ig, "");
4152
}
42-
var matchedIns = importInsRegex.exec(str) || "";
53+
const matchedIns = importInsRegex.exec(str) || "";
4354
if (!matchedIns) {
4455
throw new Error("The import instruction seems to be unreadable try formatting it, for example: \n" + "import { something } from './somet.js' \n ");
4556
}
4657
if (file) {
47-
let x = await import(fqfn);
48-
return Object.keys(x).map((v)=>x[v].toString()
49-
);
58+
const x = await import(fqfn);
59+
return Object.keys(x).map((v)=>x[v].toString());
5060
} else {
51-
let x = await import(matchedPath[0].replaceAll(/'|"/g, ""));
52-
return Object.keys(x).map((v)=>x[v].toString()
53-
);
61+
const x1 = await import(matchedPath[0].replaceAll(/'|"/g, ""));
62+
return Object.keys(x1).map((v)=>x1[v].toString());
5463
}
5564
}
5665
postMessage(msg) {
57-
this.worker.then((w)=>w.postMessage(msg)
58-
);
66+
this.worker.then((w)=>w.postMessage(msg));
5967
return this;
6068
}
6169
async stop() {
@@ -67,8 +75,7 @@ class Thread {
6775
URL.revokeObjectURL(this.blobURL);
6876
}
6977
onMessage(callback) {
70-
this.worker.then((w)=>w.onmessage = (e)=>callback(e.data)
71-
);
78+
this.worker.then((w)=>w.onmessage = (e)=>callback(e.data));
7279
return this;
7380
}
7481
}

Thread.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
export default class Thread<T> {
1+
/**
2+
* > Type T -> return type
3+
*
4+
* > Type K -> data type of MessageEvent
5+
*/
6+
export default class Thread<T = unknown, K = unknown> {
27
public worker: Promise<Worker>;
38
private imports: Array<string>;
49
private blob: Promise<Blob>;
5-
private blobURL: string = "";
10+
private blobURL= "";
611
/**
712
* Tells if the worker has been stopped
813
*/
@@ -13,7 +18,7 @@ export default class Thread<T> {
1318
* @param imports Modules to import in the worker. only JS files allowed (over the net import allowed)
1419
*/
1520
constructor(
16-
operation: (e: MessageEvent, globalObject?:{}) => T,
21+
operation: (e: MessageEvent<K>, globalObject?: Record<string, unknown>) => T | Promise<T>,
1722
type?: "classic" | "module",
1823
imports?: Array<string>,
1924
) {
@@ -24,7 +29,6 @@ export default class Thread<T> {
2429
});
2530
this.imports = imports || [];
2631
this.blob = this.populateFile(operation);
27-
this.blob.then(async (b)=>console.log(await b.text()));
2832
this.worker = this.makeWorker(type);
2933
}
3034

@@ -38,16 +42,17 @@ export default class Thread<T> {
3842
);
3943
}
4044

45+
// deno-lint-ignore ban-types
4146
private async populateFile(code: Function) {
42-
let imported = this.imports?.flatMap(async (val) => (await this.copyDep(val)).join("\n"));
47+
const imported = this.imports?.flatMap(async (val) => (await this.copyDep(val)).join("\n"));
4348
return new Blob([`
4449
${(await Promise.all(imported)).join("\n")}
4550
4651
var global = {};
4752
var userCode = ${code.toString()}
4853
49-
onmessage = function(e) {
50-
postMessage(userCode(e, global));
54+
onmessage = async function(e) {
55+
postMessage(await userCode(e, global));
5156
}
5257
5358
`]);
@@ -58,11 +63,11 @@ export default class Thread<T> {
5863
* @param str the import line (eg: import {som} from "lorem/ipsum.js";)
5964
*/
6065
private async copyDep(str: string) {
61-
var importPathRegex = /('|"|`)(.+\.js)(\1)/ig; // for the path string ("lorem/ipsum.js")
62-
var importInsRegex = /(import( |))({.+}|.+)(from( |))/ig; // for the instruction before the path (import {som} from)
63-
var matchedPath = importPathRegex.exec(str) || "";
64-
var file = false;
65-
var fqfn = "";
66+
const importPathRegex = /('|"|`)(.+\.js)(\1)/ig; // for the path string ("lorem/ipsum.js")
67+
const importInsRegex = /(import( |))({.+}|.+)(from( |))/ig; // for the instruction before the path (import {som} from)
68+
const matchedPath = importPathRegex.exec(str) || "";
69+
let file = false;
70+
let fqfn = "";
6671

6772
if (
6873
!matchedPath[0].includes("http://") &&
@@ -71,7 +76,7 @@ export default class Thread<T> {
7176
file = true;
7277
fqfn = matchedPath[0].replaceAll(/('|"|`)/ig, "");
7378
}
74-
var matchedIns = importInsRegex.exec(str) || ""; // matchedIns[0] > import {sss} from
79+
const matchedIns = importInsRegex.exec(str) || ""; // matchedIns[0] > import {sss} from
7580

7681
if (!matchedIns) {
7782
throw new Error(
@@ -82,10 +87,10 @@ export default class Thread<T> {
8287

8388

8489
if (file) {
85-
let x = await import(fqfn); //Deno.realPathSync(fqfn)
90+
const x = await import(fqfn); //Deno.realPathSync(fqfn)
8691
return Object.keys(x).map((v)=>x[v].toString())
8792
} else {
88-
let x = await import(matchedPath[0].replaceAll(/'|"/g,""));
93+
const x = await import(matchedPath[0].replaceAll(/'|"/g,""));
8994
return Object.keys(x).map((v)=>x[v].toString())
9095
}
9196
}
@@ -94,7 +99,7 @@ export default class Thread<T> {
9499
* Sends data to the Thread
95100
* @param msg
96101
*/
97-
public postMessage(msg: any): this {
102+
public postMessage(msg: K): this {
98103
this.worker.then(w=>w.postMessage(msg));
99104
return this;
100105
}

egg.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"stable": true,
55
"homepage": "https://github.com/duart38/Thread",
66
"entry": "./Thread.ts",
7-
"version": "3.0.0",
8-
"releaseType": "patch",
7+
"version": "4.0.0",
8+
"releaseType": "major",
99
"files": [
1010
"./LICENSE",
1111
"./README.md",
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import Thread from "../Thread.ts";
22

3-
let count = 13;
3+
const count = 13;
44

5-
function postMessage(e: any) {}
5+
function postMessage(_e: unknown) {}
66

77
function tester() {
8-
let i = 0;
98
setInterval(() => {
109
postMessage(0);
1110
}, 500);
@@ -14,6 +13,6 @@ function tester() {
1413
}
1514

1615
for (let i = 0; i < count; i++) {
17-
new Thread(tester, "module").onMessage((d) => console.log(`thread -> ${i}`))
16+
new Thread(tester, "module").onMessage((_) => console.log(`thread -> ${i}`))
1817
.postMessage(0);
1918
}

examples/example_async_support.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Thread from "../Thread.ts";
2+
3+
/**
4+
* Thanks to @praswicaksono for the suggestion
5+
* -> https://github.com/praswicaksono
6+
* -> https://github.com/duart38/Thread/issues/3
7+
*/
8+
9+
const thread = new Thread<number, number[]>(async (e) => {
10+
console.log("Worker: Message received from main script");
11+
const result = e.data[0] * e.data[1];
12+
await new Promise((resolve) => setTimeout(resolve, 5 * 1000))
13+
if (isNaN(result)) {
14+
return 0;
15+
} else {
16+
console.log("Worker: Posting message back to main script");
17+
return (result);
18+
}
19+
}, "module");
20+
21+
thread.onMessage((e) => {
22+
console.log(`recived back from thread: ${e}`);
23+
});
24+
25+
thread.postMessage([10, 12]);
26+
thread.postMessage([10, 10]);

examples/example_calculateprimes.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import Thread from "../Thread.ts";
22

3-
let count = 2; // number of threads to spawn
3+
const count = 2; // number of threads to spawn
44

5-
function postMessage(e: any) {} // stops the compiler from complaining that the method is not available.. this gets pasted in the worker
5+
function postMessage(_e: unknown) {} // stops the compiler from complaining that the method is not available.. this gets pasted in the worker
66

77
function tester() {
88
function calculatePrimes() {
99
const iterations = 50;
1010
const multiplier = 100000000000;
11-
var primes = [];
12-
for (var i = 0; i < iterations; i++) {
13-
var candidate = i * (multiplier * Math.random());
14-
var isPrime = true;
15-
for (var c = 2; c <= Math.sqrt(candidate); ++c) {
11+
const primes = [];
12+
for (let i = 0; i < iterations; i++) {
13+
const candidate = i * (multiplier * Math.random());
14+
let isPrime = true;
15+
for (let c = 2; c <= Math.sqrt(candidate); ++c) {
1616
if (candidate % c === 0) {
1717
// not prime
1818
isPrime = false;

examples/example_deno_worker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import Thread from "../Thread.ts";
22
import Observe from "https://raw.githubusercontent.com/duart38/Observe/master/Observe.ts";
33

4-
let tr = new Thread(
4+
const tr = new Thread<string, string>(
55
(e) => {
6-
let t = new Observe(e.data); // observable values
6+
const t = new Observe(e.data); // observable values
77
return t.getValue();
88
},
99
"module",

examples/example_importing.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Thread from "../Thread.ts";
22
import { CallMe } from "../test_import.js";
33

4-
let tr = new Thread(
5-
(e) => {
4+
const tr = new Thread(
5+
(_e) => {
66
CallMe();
77
return "pong";
88
},

examples/example_simple.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Thread from "../Thread.ts";
22

3-
let thread = new Thread<number>((e: MessageEvent) => {
3+
const thread = new Thread<number, number[]>((e) => {
44
console.log("Worker: Message received from main script");
55
const result = e.data[0] * e.data[1];
66
if (isNaN(result)) {

0 commit comments

Comments
 (0)