Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active January 27, 2023 18:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/14e5cc5dbe5bb38a9d80f25f54061c7f to your computer and use it in GitHub Desktop.
Save dfkaye/14e5cc5dbe5bb38a9d80f25f54061c7f to your computer and use it in GitHub Desktop.
embedded web workers: create a web worker dynamically with an embedded worker blob.
// 13 May 2022
// Create web worker dynamically with an embedded worker blob.
// No need to create a static file and load it with a call to
// `new Worker(url)`.
// 17 July 2022
// Changed fetch(url) example to return response.blob() and create a new child worker that
// executes the remote script.
// Related to:
// 1. Pass functions to workers: https://gist.github.com/dfkaye/527f163b6913b163a579bbeb01858593
// 2. Share data between multiple view client elements with embedded workers and custom events,
// https://gist.github.com/dfkaye/5e80b390697b60e34861a85185f9f1b6
// 28 May 2022:
// Because of the Content Security Policy issues that *may* hinder this approach,
// I have listed an alternative approach to passing functions as blob contents
// at https://gist.github.com/dfkaye/ca9722cd247c6d907b3bbaf7273741e0
// Modified example from mozilla developer network, "User Web Workers":
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#embedded_workers
// Could use in batch.js:
// https://gist.github.com/dfkaye/5f37843331f1f9cac9246f0c9521862e
// Currently fails to load if your site's Content Security Policy specifies a
// worker-src with URL sources but not the blob: scheme or strict-dynamic with
// a nonce.
// This example calls the Function constructor, a form of `eval()` which is normally
// prohibited by script-src, or script-src-elem, when they omit 'unsafe-eval'.
// Omitting is good practice, by the way. Prior exploration from 2020 gist at
// https://gist.github.com/dfkaye/da49b6c05aed48e6dfb28a2c7e87cf06
// Consult the CSP valid sources pages at
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources
// For more on script-src CSP and strict-dynamic with nonce attributes, consult
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src.
// For more on the Function constructor, consult
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function
var script = `
// Yes!
// You can fetch remote script files and process them as text,
// or as blob files.
// This fetches a one-line script on my blog, and runs it in a new
// child worker.
fetch("https://dfkaye.com/js/hello.js")
.then(res => res.blob())
.then(blob => URL.createObjectURL(blob))
.then(url => new Worker(url))
.catch(e => console.warn(e));
// A local response value.
var response = "worker response";
// Handle message as a request.
onmessage = function(request) {
console.log("request", request.data);
// Fake operator collection for the nonce...
var sum = new Function('a', 'b', 'console.log("args:", a, b); return a + b');
var ops = { sum };
// Reference our operator from the collection (doesn't handle the "not found" case)
var value = ops[request.data.op].apply('', request.data.args);
console.log("Function sum", value);
// Send response.
postMessage(["Hello from", response, "Sum of", request.data.args, "=", value])
}
`;
// Blobs require serializable objects or arrays of content.
var blob = new Blob([script], {type: 'text/javascript'});
// Handle to our blob URL.
var url = window.URL.createObjectURL(blob);
// Create our worker from our handle.
var worker = new Worker(url);
// Receive messages from the worker.
worker.onmessage = function(response) {
console.warn('Received: ' + response.data.join(" "));
};
// Send messages to the worker.
worker.postMessage({ op: "sum", args: [2, 6] });
// Here's our trace....
// request { op: "sum", args: [2, 6] }
// args: 2 6
// Function sum 8
// Received: Hello from worker response Sum of 2,6 = 8
// remote script /* hello.js */
/*
Workers allow you to:
1. perform expensive thread-blocking computations off the main thread,
2. perform async tasks, such as data requests or posts, without using
promises in the main thread.
Possibilities:
1. fetch multiple js files as text and join them together,
2. test each js file separately by regular import in main thread (i.e., using mocha),
3. perform batch computations with Function() constructor (spreadsheet eval, e.g.),
4. access indexeddb,
5. view and/or instrument the worker script element text with browser inspect tools,
6. support a *micro-frontend* architecture, such as...
The SAM Pattern:
1. The entire action-model-state-nextAction loop runs entirely in the worker(s),
2. The web document contains one or more client elements their own UI orchestration
that does not concern the loop,
3. The multiple-app problem becomes:
1. each client element manages its own startup,
2. each client element manages its own worker loop,
3. undo-redo and pause-resume are commands that move to worker loops,
3. each client element can listen to other client elements via workers (not SharedWorkers)
[more to come...]
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment