Does JSAR Support Web Workers? Complete Implementation Guide
Yes, JSAR provides a full Web Workers implementation that mirrors the browser Worker API while running on top of Node.js worker_threads.
The m-creativelab/jsar-runtime repository includes a self-contained worker system that simulates the DOM Worker environment in a Node.js-based runtime. This allows JavaScript code written for browser Workers to execute unchanged within the JSAR platform.
How JSAR Implements Web Workers
JSAR does not rely on browser-native Workers. Instead, it implements the entire Worker specification using Node.js threading primitives. The architecture bridges the Node.js worker_threads module with a browser-compatible API layer, complete with DOM events, script loading, and nested worker support.
The system is modularized across four key areas:
- Worker lifecycle management – Handles construction, message routing, and termination
- Entry point bootstrapping – Sets up the global scope inside the worker thread
- Event system – Provides DOM-compatible
MessageEventandErrorEventobjects - Resource loading – Integrates with JSAR's asset pipeline for script fetching
Core Components of the Worker System
WorkerImpl: The Public Interface
The [lib/webworkers/worker.ts](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/webworkers/worker.ts) file exports the WorkerImpl class, which implements the standard Worker interface. This class exposes onmessage, onerror, onmessageerror, and methods like postMessage() and terminate().
Key implementation details from the source:
- Document requirement: The constructor checks for the existence of
documentto ensure Workers are only created within browser-like environments - Blob URL support: Detects
blob:schemes and resolves underlying Blob sources viaresolveObjectURL - Private initialization: The
#initHandle()method wires Node's'message','messageerror', and'error'events to DOM-compatible event objects
When worker.start() is called, the class resolves the internal entry script (jsar-webworkers-entry.js) and spawns a Node WorkerThreads.Worker with a serialized WorkerRequest containing the script URL and base URI.
Entry Script and WorkerContext
The [lib/webworkers/entry.ts](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/webworkers/entry.ts) file serves as the bootstrap code executed inside each worker thread. Its responsibilities include:
- Mapping
globalThis.postMessagetoparentPort.postMessagefor host communication - Injecting
__WorkerImplinto the global scope to enable nested workers (workers creating workers) - Instantiating a
WorkerContext(JSAR's DOM shim) to provideselfand other globals - Loading either inline script sources (from Blobs) or fetching external URLs via the resource loader
Event Handling
Standard DOM event objects are re-exported from [lib/webworkers/events.ts](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/webworkers/events.ts). The implementation provides:
ErrorEventfor uncaught exceptionsMessageEventfor data passing between threads
These align with the WHATWG Web Workers specification, ensuring that event handlers like worker.onmessage receive objects with data, origin, and ports properties.
Resource Loading
Workers share the same asset pipeline as the main JSAR runtime through [lib/runtime2/ResourceLoader.ts](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/ResourceLoader.ts). The ResourceLoaderOnTransmute interface allows workers to fetch scripts and assets using the same resolution logic as the parent context, including support for custom protocols and transmutation hooks.
Creating and Using Workers in JSAR
Basic Worker Instantiation
To create a worker in JSAR, instantiate WorkerImpl with a script URL, register event handlers, and call start():
import { WorkerImpl } from 'jsar-runtime/lib/webworkers/worker';
// Create worker from a script file
const worker = new WorkerImpl('scripts/computation.js');
// Handle incoming messages
worker.onmessage = (event: MessageEvent) => {
console.log('Received:', event.data);
};
worker.onerror = (event: ErrorEvent) => {
console.error('Worker failed:', event.message, event.filename);
};
// Initialize the worker thread
worker.start();
// Send data to the worker
worker.postMessage({ type: 'CALCULATE', payload: [1, 2, 3] });
Nested Workers via __WorkerImpl
JSAR supports spawning workers from within workers using the hidden __WorkerImpl global injected by the entry script:
// Inside scripts/computation.js (running in worker context)
self.onmessage = (e) => {
if (e.data.type === 'SPAWN_SUBWORKER') {
// Create a nested worker
const subWorker = new __WorkerImpl('scripts/sub-task.js');
subWorker.onmessage = (msg) => {
self.postMessage({ from: 'nested', result: msg.data });
};
subWorker.start();
subWorker.postMessage(e.data.payload);
}
};
This pattern enables parallel processing trees where parent workers delegate to child workers without blocking the main thread.
Inline Workers with Blob URLs
For dynamic script generation, JSAR supports the Blob URL pattern:
const workerScript = `
self.onmessage = (e) => {
const result = e.data.map(x => x * 2);
self.postMessage(result);
};
`;
const blob = new Blob([workerScript], { type: 'application/javascript' });
const blobUrl = URL.createObjectURL(blob);
const worker = new WorkerImpl(blobUrl);
worker.onmessage = (ev) => console.log('Doubled:', ev.data);
worker.start();
worker.postMessage([5, 10, 15]);
The WorkerImpl constructor detects the blob: protocol and extracts the underlying source code for execution in the worker context.
Summary
- JSAR implements the complete Web Workers API using Node.js
worker_threadsas the underlying engine - The
WorkerImplclass inlib/webworkers/worker.tsprovides the standard Worker interface with DOM-compatible events - Workers can spawn nested workers using the
__WorkerImplglobal available in worker contexts - Script loading supports both external URLs and inline Blob sources through the integrated ResourceLoader
- The implementation requires a DOM environment (checks for
document) to maintain browser semantics
Frequently Asked Questions
Does JSAR support Web Workers?
Yes. JSAR includes a full Web Workers implementation that mirrors the browser API. The system uses Node.js worker_threads internally while exposing standard Worker methods like postMessage(), terminate(), and event handlers. The source code in lib/webworkers/worker.ts implements this interface completely.
Can workers spawn other workers in JSAR?
Yes, JSAR supports nested workers. When a worker script executes, the entry script (lib/webworkers/entry.ts) injects __WorkerImpl into the global scope. This allows code running inside a worker to instantiate child workers using new __WorkerImpl(url), enabling hierarchical parallel processing.
How does JSAR handle worker script loading?
JSAR uses its ResourceLoader system (from lib/runtime2/ResourceLoader.ts) to resolve worker scripts. The WorkerImpl constructor accepts standard URLs or Blob URLs. For Blob sources, the implementation resolves the underlying source code via resolveObjectURL. The entry script then either evaluates the inline source or fetches the external resource through the runtime's asset pipeline.
Is the JSAR Worker API compatible with standard browser Workers?
Yes. The implementation aims for compatibility with the WHATWG Web Workers specification. Event objects (MessageEvent, ErrorEvent), the self global scope, and method signatures match browser behavior. Code written for standard browser Workers typically runs in JSAR without modification, provided it does not rely on browser-specific APIs unavailable in the JSAR runtime.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →