Table of Contents
Server-side Workers
Hop server-side workers are processes that run concurrently. Each worker possesses its own isolated memory heap. Workers cannot share JavaScript objects. Each worker possesses its own private JavaScript global objects. Worker communicate using message passing. Workers are the only mean for running concurrent JavaScript execution on the server.
Note: This page only documents server-side workers. The documentation of client-side workers can be found here.
Terminology
We define the parent thread as the thread which executes the new Worker
constructor, and the worker thread as the thread which is created by
the system in response to the new Worker
instruction.
A worker thread is attached to one and only one parent thread.
Any thread may be the parent thread of multiple worker threads.
The parent to worker threads connection graph is a tree, rooted by the main thread of process.
Constructor
new Worker( path )
Workers are created using the Worker
function which takes as parameter
the source file of the worker. The path is resolved using the same rules
as the require
module importation function
(see module).
Example:
var w = new Worker( "./slave.js" );
New Worker( path )
returns immediately, before the worker module is
fully initialized. Messages sent to the worker are buffered until the
worker is able to process them, so the parent thread may send
messages as soon as the Worker
object is created.
However, since workers may also define services, the developer should ensure that services defined within the worker will not be invoked until the worker is fully initialized. In general, this is achieved by letting the worker pass the service handle to the parent thread. Note that services that are invoked as a result of a user action on a web client are very likely to be up and running long before the user clicks on the button triggering the service invocation.
Properties
The following properties define EventListeners
for the parent
thread, attached to Worker
.
Worker.onerror
An EventListener
called whenever an error occurs on Worker
.
Worker.onexit
An EventListener
called when Worker
terminates.
Worker.onmessage
An EventListener
called whenever the parent thread receives a
message from the Worker
thread. The message is stored in the
data
property of the event.
Variables
The following variable can be set in a worker thread, it is implicitely attached to the parent thread.
onmessage
An EventListener
called whenever the worker thread receives a
message from its parent thread. The message is stored in the
data
property of the event.
Methods
Worker.postMessage( msg )
Use this Method in the parent thread to send a message to a
worker. The argument msg
is a JavaScript object which is donated
to the target. Donation depends on the type of the objects. The
following rules apply:
- If
msg
is an immediate value, the value is transmitted as is. - If
msg
is anArray
, anArrayBuffer
, anArrayBufferView
, or aTypedBuffer
, the array is donated to the target worker. All the values are donated to the target worker. The source worker is left with an array of length 0. - In all other cases,
msg
is deep copied and each field is donated to the target. Service
handles can be passed within a message.- Objects made from the above data types are passed with the same donation rules.
- Objects that cannot be donated (
Worker
,Arguments
,Function
,Math
,Json
,Error
, orPromise
) are silently replaced with theundefined
value.
postMessage( msg )
Use this function in the Worker thread to send a message to the implicit parent.
Basic communications
This example shows how to spawn server-side worker and how the master and slave worker communicate.
worker/worker.js
console.log( "starting worker-master..." );
var bar = require( "./bar.js" );
var w = new Worker( "./slave.js" );
w.onmessage = function( e ) {
console.log( "received from the slave: ", e.data );
console.log( "onmsg master bar=", bar.count( 1 ), " (expect 16)" );
}
w.postMessage( "hello slave" );
console.log( "master, bar=", bar.count( 10 ), " (expect 10)" );
console.log( "master, bar=", bar.count( 5 ), " (expect 15)" );
console.log( "master done...");
worker/slave.js
console.log( "starting worker-slave..." );
var bar = require( "./bar.js" );
onmessage = function( e ) {
console.log( "received from master: ", e.data );
postMessage( "what master?" );
}
postMessage( "hi master" );
console.log( "slave bar=", bar.count( 100 ), " (expect 100)" );
console.log( "slave bar=", bar.count( 3 ), " (expect 103)" );
worker/bar.js Note that the
parent thread and the Worker thread each get their instance of
bar.js
.
console.log( "bar.js" );
var count = 0;
function count_inc( v ) {
count += v;
return count;
}
exports.count = count_inc;
Worker.terminate()
Terminates Worker
as soon as possible. Sub Workers are recursively
terminated.
close()
Use the function close
to self terminate a Worker. Sub Workers
are recursively terminated.
This example shows how to terminate workers.
worker2/worker2.js
console.log( "Master creating a Worker" );
var w = new Worker( "./slave.js" );
// can post as soon as the Worker object is created.
console.log( "Master sending a Work Order" );
w.postMessage( "Work Order" );
w.onmessage = function ( e ) {
console.log( "Master received '%s'", e.data );
console.log( "Master terminating worker" );
w.terminate();
};
//w.onexit is useful to track worker self termination (not the case here).
w.onexit = function() {
console.log( "Worker is terminated, I knew that already" );
}
worker2/slave.js
console.log( "Worker: starting" );
onmessage = function( e ) {
console.log( "Worker received '%s'", e.data );
console.log( "Worker processing ..." );
setTimeout( function() {
console.log( "Worker sending Report" );
postMessage( "Report" );
}, 1000 );
};
Workers and Services
This example demonstrates advanced use of JavaScript workers. The main
task is responsible to initiate two slave twin workers running the
same code, and create a browser service launch both workers in
iframe
s. When launched, each slave worker defines a dynamic service
and sends a service handle to the main thread to let the thread build
the main html page.
Service handles, just like other hop.js objects, can be sent as worker messages.
Since there is a unique name space for services, all services created
in slave.js
are anonymous. Hop ensures that unique names are
generated for each service.
Note that each slave.js
worker operates its private counter. The same
isolation property would apply to submodules required by slave.js
.
Note: the worker5 service cannot be invoked until both slave workers are fully initialized, which is not an issue since the service is invoked from the client browser only.
Putting the createWorker calls within the main service would require an asynchronous coding style where the service implementation creates slave workers and returns only after the worker services are created.
worker5/worker5.js
function createWorker( title ) {
var w = new Worker( "./slave.js" );
w.onmessage = function( message ) {
w.svc = message.data;
console.log( 'main: service handle received from %s', title );
}
w.title = title;
console.log( 'main: %s created', title );
return w;
}
var w1 = createWorker( 'left worker' );
var w2 = createWorker( 'right worker' );
service worker5() {
return new Promise( function( resolve, reject ) {
setTimeout( function() {
resolve( <html>
<div>
<iframe style="border: 1px solid black" src=${w1.svc( w1.title )}/>
<iframe style="border: 1px solid black" src=${w2.svc( w2.title )}/>
</div>
</html> ) }, 1000 );
} );
}
console.log( "Go to \"http://%s:%d/hop/worker5\"", hop.hostname, hop.port );
worker5/slave.js
console.log( "starting worker-slave..." );
var counter = 0;
var svc = service( title ) {
console.log( '%s service invoked: building html frame', title );
var count = <span id="counter">${counter}</span>;
return <html>
<h1>${title}</h1>
<div>counter: ${count}</div>
<button onclick=~{
${service () {
console.log( '%s counter: set value to %d', title, counter + 1 );
return ++counter;
}}().post( function( v ) { ${count}.innerHTML = v } )
}>
inc me
</button>
</html>
}
onmessage = function( message ) {
console.log( "slave worker: message received from main thread: ignoring ",
message.data );
}
postMessage( svc );
console.log( 'slave worker: service handle sent to main thread' );