Last active
June 13, 2024 07:34
-
-
Save jozefchutka/d6be7049f764c11dc059029908cb6ed9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script> | |
| /* | |
| thread() is a function that allows you to execute any isolated function asynchronously in a worker thread. | |
| await thread(myFunction, [arg1, arg2, ...]) | |
| See more examples and tests below using async functions, transfers, aborts, concurrency,. | |
| */ | |
| function thread(func, args, options) { | |
| return new Promise((resolve, reject) => { | |
| function terminate() { | |
| worker.terminate(); | |
| URL.revokeObjectURL(url); | |
| } | |
| const payload = ` | |
| onmessage = async ({data:{args, transferFromWorker}}) => { | |
| const r = await (${func.toString()}).apply(this, args); | |
| const t = transferFromWorker?.map(i => i.split(".").reduce((a, c) => a[c], r)); | |
| postMessage(r, t); | |
| } | |
| onunhandledrejection = onerror = e => { | |
| e.preventDefault(); | |
| throw e.type; | |
| }`; | |
| const url = URL.createObjectURL(new Blob([payload], {type:"text/javascript"})); | |
| const worker = new Worker(url); | |
| worker.onmessage = ({data}) => { | |
| terminate(); | |
| resolve(data); | |
| } | |
| worker.onerror = worker.onmessageerror = async () => { | |
| terminate(); | |
| try { | |
| resolve(await func.apply(this, args)); | |
| } catch(error) { | |
| reject(error); | |
| } | |
| } | |
| worker.postMessage({args, transferFromWorker:options?.transferFromWorker}, options?.transferToWorker); | |
| const signal = options?.signal; | |
| signal?.addEventListener("abort", () => { | |
| terminate(); | |
| reject(signal.reason); | |
| }) | |
| }) | |
| } | |
| (async () => { | |
| // TEST Simple function | |
| const result1 = await thread((a, b, c) => a + b + c, [1, 2, 3]); | |
| console.log("test1", result1); | |
| // TEST Function that uses transfer | |
| const test2 = source => { | |
| const result = new Uint8Array(source.byteLength * 2); | |
| result.set(source); | |
| result.set(source, source.byteLength); | |
| return {myWrapper:{nested:result}, source}; | |
| } | |
| const array = new Uint8Array([1, 2, 3]); | |
| const result2 = await thread(test2, [array], { | |
| transferToWorker: [array.buffer], | |
| transferFromWorker: ["myWrapper.nested.buffer", "source.buffer"] | |
| }) | |
| console.log("test2", result2); | |
| // TEST Async function | |
| async function test3() { | |
| await new Promise(r => setTimeout(r, 100)); | |
| return "asyncResult"; | |
| } | |
| const result3 = await thread(test3); | |
| console.log("test3", result3); | |
| // TEST Cancellable function | |
| async function test4() { | |
| await new Promise(r => setTimeout(r, 5000)); | |
| return 789; | |
| } | |
| const controller = new AbortController(); | |
| setTimeout(() => controller.abort("took too long"), 100); | |
| const result4 = await thread(test4, undefined, {signal:controller.signal}) | |
| .catch(reason => console.log("test4", `Expected abort ${reason === "took too long"}`)); | |
| // TEST Not executable in Worker | |
| const test5 = () => document.documentElement.innerHTML; | |
| const result5 = await thread(test5); | |
| console.log("test5", `length=${result5.length}`); | |
| // TEST Concurrency | |
| async function test6(i) { | |
| await new Promise(r => setTimeout(r, 100)); | |
| return i; | |
| } | |
| const result6 = await Promise.all([ | |
| thread(test6, [1]), thread(test6, [2]), thread(test6, [3]), thread(test6, [4]), | |
| thread(test6, [5]), thread(test6, [6]), thread(test6, [7]), thread(test6, [8])]); | |
| console.log("test6", result6); | |
| })() | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment