The framework allows spawning Workers from inline functions, with automatic injection of the required modules.
The examples below assume that the cited modules (
hex,lz4,b64,processMessage,processRPC, …) have been registered beforehand viafw.runtime.registerAll(modules)or by manual composition.main.jsregisters nothing automatically — see Quick start.
Principle
main.js → createWorker(fn, options)
↓
serialize modules → Blob URL → new Worker(blobUrl)
↓ (inside the worker)
ModuleRuntime + registered modules
workerFw(runtime, modules, args) → context
workerFn(context)
Full signature
const worker = fw.createWorker(workerFn, {
dependencies: ['hex', 'lz4'], // required modules
workerFw: myFrameworkFn, // optional — custom bootstrap
args: ['param1', 42], // optional — JSON-serialisable
terminate: () => cleanup() // optional — cleanup callback
});
See createWorker API for the full reference.
Typical use cases
1. Data processing
const worker = fw.createWorker(
function({ libs, args }) {
const [data] = args;
const compressed = libs.lz4.compress(new Uint8Array(data));
self.postMessage({ compressed: Array.from(compressed[1]) });
},
{
dependencies: ['lz4'],
args: [largeArrayBuffer]
}
);
worker.onmessage = ({ data }) => {
console.log('compressed:', data.compressed.length, 'bytes');
};
2. Inline parallel execution
Same logic, without a worker — useful for debugging and testing:
const workerFn = function({ libs, args }) {
return libs.hex.fromBytes(args[0]);
};
// Inline
const libs = fw.runtime.resolveAll(['hex'], { instances: new Map() });
const result = workerFn({ libs, process: {}, args: [new Uint8Array([255])] });
// Worker (same function)
const worker = fw.createWorker(workerFn, {
dependencies: ['hex'],
args: [Array.from(new Uint8Array([255]))] // JSON-serialisable
});
3. Bidirectional messages with processMessage
// Main thread
const { processMessage } = fw.runtime.resolveAll(['processMessage']);
const workerRef = fw.createWorker(
function({ libs, process }) {
// Register a command handler
process.doWork = function(data) {
const result = libs.b64.fromBytes(new Uint8Array(data));
process.postMessage({ result });
};
},
{
dependencies: ['processMessage', 'b64'],
workerFw: processMessage.workerFramework
}
);
const cmd = processMessage.workerCommand(workerRef);
cmd.doWork([72, 101, 108]); // sends the command
workerRef.onmessage = ({ data }) => console.log(data.result);
4. RPC (Remote Procedure Call)
A MessagePort is a Transferable, not a JSON-serialisable value:
it cannot therefore pass through options.args (see Constraints). It must be
transferred explicitly via postMessage.
// Object to expose from the main thread
const api = {
add: (a, b) => a + b,
format: { hex: (bytes) => fw.runtime.resolve('hex').fromBytes(bytes) }
};
const { processRPC } = fw.runtime.resolveAll(['processRPC']);
const { port } = processRPC.create(api);
const worker = fw.createWorker(
function({ libs }) {
// The port arrives by explicit transfer — not via `args`.
self.onmessage = async (event) => {
const rpc = await libs.processRPC.open(event.ports[0]);
const sum = await rpc.add(10, 32); // → 42
const str = await rpc.format.hex([255, 0]); // → "ff00"
self.postMessage({ sum, str });
};
},
{
dependencies: ['processRPC']
}
);
// Transfer the port to the worker (2nd argument = list of Transferables)
worker.postMessage(null, [port[0]]);
The reverse direction (worker exposes the API, main thread consumes) also works:
the worker calls libs.processRPC.create(api) then
self.postMessage({ rpcReady: true }, [port[0]]), and the main thread opens
event.ports[0] with processRPC.open(...).
Constraints
| Constraint | Reason |
|---|---|
workerFn must be self-contained |
Converted to string via .toString() |
args must be JSON-serialisable |
Transmitted via JSON.stringify/parse |
| No closures over main-thread variables | Not serialisable |
No document/window in the factory |
Not available inside workers |
Cleanup
worker.terminate(); // patched → revokeObjectURL + callback + native terminate
Idempotent — multiple calls are no-ops.
Worker logs
If ENV.LOG = true, console.log/warn/error calls inside the worker are automatically forwarded to fw.log. They appear in fw.log.get() with the appropriate lvl.