Weaver.js
Introduction
Expensive calculations can make your app painfully slow for your users. You can make parallelisable code much faster by using Weaver.js, making your app snappier and your users happier.
Factsheet
- Allows for both low level thread management and higher level thread management via fabrics
- Permissive open source license (MIT)
- Dependency free
- Compatible with
- All modern browsers
- CommonJS/Node.js
- AMD/Require.js
- npm
- Bower
- spm
- Meteor/Atmosphere
- Has a full suite of unit tests that can be run in the browser or the terminal
- Documentation includes live code examples, doubling as an interactive requirements specification
- Threads and fabrics are reusable
- Allows passing data to threads and fabrics
- Allows requiring external functions
- Uses promises to make managing your asynchronous workflows easier (and includes a small, internal polyfil for promiseless browsers)
- Includes parallel versions of popular array functions
- Chainable for convenience
- Falls back on no-threads implementation for environments that do not support parallelism
- Well maintained, with only a sliver of active bug time (i.e. minimised time to bugfix)
About
Weaver.js is an open-source parallelism library written in JavaScript. You can use Weaver.js for speeding up your code where parallelisation applies.
Weaver.js is an open-source project, and anyone is free to contribute. For more information, refer to the GitHub README.
Weaver.js was designed and developed by Max Franz.
The .js.org domain for Weaver is provided gratis by JS.ORG.
Packages
- npm :
npm install weaverjs
- Bower :
bower install weaverjs
- spm :
spm install weaverjs
- Meteor/Atmosphere :
meteor add maxkfranz:weaver
Events
Event object
Events passed to handler callbacks are similar to jQuery event objects in that they mimic the API of native event objects.
Fields:
type
: the event type string (e.g.'stop'
)namespace
: the event namespace string (e.g.'foo'
for'foo.tap'
)data
: additional data object passed by.trigger()
timeStamp
: Unix epoch time of event
Thread events
run
: when a thread starts a runran
: when a thread ends a runstop
: when a thread is stopped (i.e. terminated and no longer usable)message
: when a thread receives a message
Thread
A thread is a reusable object for purposes of parallelism. It gives a simple and concise API that is consistent across JS environments (using WebWorkers on browsers and forked child processes on Node.js).
A thread can be created as follows:
var thread = weaver.thread();
The new
keyword and class-style uppercase naming are optional, so you may alternatively do as follows:
var thread = new weaver.Thread();
Minification
When code is run through a minifer like UglifyJS, the names of variables can be mangled. This means that the name specified in your code for a function or object may not be the same when it is minified.
Because Weaver is limited to what JS engines provide in terms of live code inspection, this means that minification can break the references made in thread.run()
.
In order to minify Weaver-using code, it is necessary to have more fastidious use of references. By explicitly naming references using thread.require( obj, 'obj' )
and _ref_( 'obj' )
, minification will work properly.
Globals
The functions that follow are globally accessible inside of thread code (but private to the thread):
Get the specified variable in the thread's scope, preventing minification issues.
- name
The name of the variable to get.
Description
The purpose of this function is to reference a variable in the thread explicitly. This avoids minification from mangling references to things you have required via thread.require()
or to thread globals like resolve()
or broadcast()
.
Examples
function foo(){
return 'bar';
}
var t = weaver.thread();
t.require( foo, 'foo' ); // explicitly specify name to avoid minification issues
t.run(function(){
// explicitly get by reference to avoid minification issues:
var foo = _ref_('foo');
var resolve = _ref_('resolve');
resolve( foo() );
}).then(function( val ){
console.log( val ); // bar
});
Resolve the thread's run promise with a single value such that other entities (like the main JS thread or other threads) can read it.
Details
This function allows the developer to pass a single value outside of the thread.
Examples
var t = weaver.thread();
t.promise(function(){
resolve( 3 );
}).then(function( val ){
console.log( 'thread resolved with `%s`', val );
t.stop();
});
Reject the thread's run promise with a single error value such that other entities (like the main JS thread or other threads) can read it.
Details
This function allows the developer to pass a single error value outside of the thread.
Examples
var t = weaver.thread();
t.promise(function(){
reject( 'some reason' );
}).then(function( val ){
// does not resolve
}, function( err ){
console.log( 'thread rejected with `%s`', err );
t.stop();
});
Listen to messages sent to the thread.
- function(msg)
A function that is called when a message,
msg
, is received.
Examples
var t = weaver.thread();
t.run(function(){
listen(function( msg ){
console.log( 'thread heard: ' + [msg.foo, msg.bar].join(' ') );
broadcast( msg ); // just send it back
});
});
t.on('message', function( e ){
var msg = e.message;
console.log( 'main js entity/thread heard: ' + [msg.foo, msg.bar].join(' ') );
t.stop();
});
t.message({ foo: 'hello', bar: 'world' });
message()
,Broadcast a message to entities who are listening to the thread.
- msg
A message, e.g. JSON object or string, that is broadcasted.
Examples
var t = weaver.thread();
t.run(function(){
listen(function( msg ){
console.log( 'thread heard: ' + [msg.foo, msg.bar].join(' ') );
broadcast( msg ); // just send it back
});
});
t.on('message', function( e ){
var msg = e.message;
console.log( 'main js entity/thread heard: ' + [msg.foo, msg.bar].join(' ') );
t.stop();
});
t.message({ foo: 'hello', bar: 'world' });
Execution
thread.include()
,Require a named function into the thread, giving the thread a global reference to the function. Plain objects or simple objects with a prototype (that can be inpected and serialised live) are also supported. Specifying the name of a JS file (i.e. a string ending in '.js') allows for importing external files into the global thread scope.
- obj
The named function, simple object, or JS file name to require that is serialised and copied to the thread.
- as [optional]
The name of the object as available in the thread. This is mandatory for an object that is not a named function, because its name can not be automatically inferred. This is also mandatory if minification is used, to prevent mangling.
Details
This function allows for pulling external code into the thread. A thread can require
- a plain JSON-serialisable object,
- a function that does not reference externals (external functions or external objects),
- a function that references externals which have also been required,
- a simple object with a prototype, and
- external JS files.
To avoid issues with minification, it is necessary to explicitly specify the name of a required entity, e.g. thread.require( foo, 'foo' )
.
Examples
var t = weaver.thread();
function foo(){
return 'bar';
}
t.require( foo );
t.run(function(){
var ret = foo();
console.log( 't::foo() return value: ' + ret );
broadcast( ret );
});
t.on('message', function( e ){
var msg = e.message;
var ret = msg;
console.log( 'return value as heard by main JS thread/entity: ' + ret );
t.stop();
});
Pass data to the thread such that it is accessible within thread.run()
etc callback functions.
- passed
JSON serialisable data that is passed to
thread.run()
.
Details
This function allows for sending data to the thread. The data must be serialisable via JSON.stringify()
.
Examples
var t = weaver.Thread();
t.pass( { foo: 1, bar: 2 } ).run(function( data ){
data.foo++;
data.bar++;
resolve(data);
}).then(function( data ){
console.log( data );
t1.stop();
});
thread.promise()
,Run the specified function in the thread, returning a promise that is resolved when the thread calls resolve()
or when the specified function returns a value.
- function( passed )
The function which will be copied and run in the thread.
Details
Please note that the passed function to thread.run()
is copied to the thread, and so it is not possible to make use of references that originate outside of the function itself. That is, a thread has its own isolated memory space.
Examples
var t = weaver.thread();
t.run(function(){
resolve( 3 );
}).then(function( val ){
console.log( 'resolved value: ' + val );
t.stop();
});
Get a mapping from the thread using the specified mapping function that uses resolve()
or a return value to specify the mapped value for each element.
- function( currVal, index, array )
A mapping function that specifies the resultant mapped value via
resolve()
or a return value.- currVal
The current element being processed in the passed array.
- index [optional]
The index of the current element being processed in the passed array.
- array [optional]
The passed array
map()
was called upon.
- currVal
Examples
var t = weaver.Thread();
t.pass([ 1, 2, 3 ]).map(function( n ){
return n*n;
}).then(function( data ){
console.log( data ); // [1, 4, 9]
t1.stop();
});
Apply a function against an accumulator and each value of the passed array (from left-to-right) has to reduce it to a single value. The function specifies the resultant value via resolve()
or a return value.
- function( prevVal, currVal, index, array )
The accumulator function.
- prevVal
The value previously returned in the last invocation of the accumulator function, or initVal if supplied.
- currVal
The current element being processed in the passed array.
- index
The index of the current element being processed in the passed array.
- array
The passed array
reduce()
was called upon.
- prevVal
- initVal [optional]
Value to use as the first argument to the first call of the accumulator function.
Apply a function against an accumulator and each value of the passed array (from right-to-left) has to reduce it to a single value. The function specifies the resultant value via resolve()
or a return value.
- function( prevVal, currVal, index, array )
The accumulator function.
- prevVal
The value previously returned in the last invocation of the accumulator function, or initVal if supplied.
- currVal
The current element being processed in the passed array.
- index
The index of the current element being processed in the passed array.
- array
The passed array
reduceRight()
was called upon.
- prevVal
- initVal [optional]
Value to use as the first argument to the first call of the accumulator function.
thread.halt()
, thread.terminate()
,Terminates the thread such that it no longer runs.
Events
For events of evt.type === 'message'
, the message itself can be found in evt.message
.
Send the thread a message.
- msg
The message (e.g. JSON object or string) to send to the thread.
Examples
var t = weaver.thread();
t.run(function(){
listen(function( msg ){
console.log( 'thread heard: ' + [msg.foo, msg.bar].join(' ') );
broadcast( msg ); // just send it back
});
});
t.on('message', function( e ){
var msg = e.message;
console.log( 'main js entity/thread heard: ' + [msg.foo, msg.bar].join(' ') );
t.stop();
});
t.message({ foo: 'hello', bar: 'world' });
thread.bind()
, thread.listen()
, thread.addListener()
,Bind to events that are emitted by the thread.
- events
A space separated list of event names.
- data [optional]
A plain object which is passed to the handler in the event object argument.
- function(evt)
The handler function that is called when one of the specified events occurs.
- evt
The event object.
- evt
- eventsMap
A map of event names to handler functions.
- data [optional]
A plain object which is passed to the handler in the event object argument.
Examples
var t = weaver.thread();
t.on('run', function(){
console.log('run');
});
t.pass([1, 2, 3]).run(function( arr ){
return arr[0] + arr[arr.length - 1];
}).then(function( res ){
console.log('got run response: ' + res);
t.stop();
});
thread.pon()
,Get a promise that is resolved when the first of any of the specified events is triggered on the thread.
- events
A space separated list of event names.
Examples
var thread = weaver.thread();
thread.pon('ran').then(function(){
console.log('thread ran promise resolved');
});
thread.run(function(){
resolve('thread has finished');
}).then(function(){
thread.stop();
});
Bind to events that are emitted by the thread, and trigger the handler only once.
- events
A space separated list of event names.
- data [optional]
A plain object which is passed to the handler in the event object argument.
- function(evt)
The handler function that is called when one of the specified events occurs.
- evt
The event object.
- evt
- eventsMap
A map of event names to handler functions.
- data [optional]
A plain object which is passed to the handler in the event object argument.
Examples
var t = weaver.thread();
t.one('run', function(){
console.log('run (once)');
});
t.run(function(){
return 'some response';
}).then(function( res ){
console.log('got run x1 response: ' + res);
return t.run(function(){
return 'some other message';
});
}).then(function( res ){
console.log('got run x2 response: ' + res);
t.stop();
});
thread.unbind()
, thread.unlisten()
, thread.removeListener()
,Remove event handlers on the thread.
- events
A space separated list of event names.
- handler [optional]
A reference to the handler function to remove.
- eventsMap
A map of event names to handler functions to remove.
Examples
var t = weaver.thread();
t.on('run', function(){
console.log('run');
});
t.run(function(){
return 'the first response';
}).then(function( res ){
console.log('got run response: ' + res);
t.off('run'); // so the 2nd one isn't triggered...
}).then(function(){
return t.run(function(){
return 'the second response';
});
}).then(function(){
t.stop();
});
thread.emit()
,Trigger one or more events on the thread.
- events
A space separated list of event names to trigger.
- extraParams [optional]
An array of additional parameters to pass to the handler.
Examples
var t = weaver.thread();
t.on('foo', function(){
console.log('foo!');
});
t.trigger('foo');
Fabric
A fabric is a collection of threads that can be used together in a collaborative fashion for parallelism purposes, thereby increasing the overall speed of execution of parallel code. A fabric may be created as follows:
var fabric = weaver.fabric();
The new
keyword and class-style uppercase naming are optional, so you may alternatively do as follows:
var fabric = new weaver.Fabric();
For most usecases, a developer would use a fabric rather than individual threads. By default, a fabric parallelises its tasks among its threads, making your code faster. A task that is not suitable to parallelism in this manner (like .reduce()
) is run in a single thread of the fabric, with the fabric acting as a queue. Because multiple tasks can be run across the threads using this queuing mechanism, your code can still experience speedup.
Execution
fabric.include()
,Require a named function into each thread in the fabric, giving a global reference to the function. Plain objects or simple objects with a prototype (that can be inpected and serialised live) are also supported. Specifying the name of a JS file (i.e. a string ending in '.js') allows for importing external files into the global thread scope.
- obj
The named function, simple object, or JS file name to require that is serialised and copied to the fabric.
- as [optional]
The name of the object as available in the fabric. This is mandatory for an object that is not a named function, because its name can not be automatically inferred.
Examples
var f = weaver.fabric();
function plusplus( n ){
return ++n;
}
f.require( plusplus ).pass([ 1, 2, 3, 4 ]).map(function( n ){
return plusplus(n);
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Pass data to the fabric such that it is accessible within fabric.spread()
etc callback functions.
- passed
JSON serialisable data that is passed to functions like
fabric.spread()
.
Examples
var f = weaver.fabric();
f.pass([ 1, 2, 3, 4 ]).map(function( n ){
return n*n;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Get a random thread from the fabric.
Examples
var f = weaver.fabric();
f.random().run(function(){
return 2 + 2;
}).then(function( sum ){
console.log('sum should be ' + sum + ' unless the year is 1984');
f.stop();
});
fabric.promise()
,Run the specified function in a random thread of the fabric, returning a promise that is resolved when the thread calls resolve()
or when the specified function returns a value.
- function( passed )
The function which will be copied and run in the fabric.
Examples
var f = weaver.fabric();
f.run(function(){
return 2 + 2;
}).then(function( sum ){
console.log('sum should be ' + sum + ' unless the year is 1984');
f.stop();
});
Get a mapping from the fabric using the specified mapping function that uses resolve()
or a return value to specify the mapped value for each element.
- function( currVal, index, array )
A mapping function that specifies the resultant mapped value via
resolve()
or a return value.- currVal
The current element being processed in the passed array.
- index [optional]
The index of the current element being processed in the passed array.
- array [optional]
The passed array
map()
was called upon.
- currVal
Examples
var f = weaver.fabric();
f.pass([ 1, 2, 3, 4 ]).map(function( n ){
return n*n;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Filters the passed array, giving a resultant array that contains the elements which pass the specified test function. The test function returns or resolve()
s to specify the test result.
- function( currVal, index, array )
The test function that returns or
resolve()
s a truthy value to specify whether the specified value passes the test.- currVal
The current element being processed in the passed array.
- index [optional]
The index of the current element being processed in the passed array.
- array [optional]
The passed array
filter()
was called upon.
- currVal
Examples
var f = weaver.fabric();
f.pass([ -2, -1, 0, 1, 2 ]).filter(function( n ){
return n > 0;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Apply a function against an accumulator and each value of the passed array (from left-to-right) has to reduce it to a single value. The function specifies the resultant value via resolve()
or a return value.
- function( prevVal, currVal, index, array )
The accumulator function.
- prevVal
The value previously returned in the last invocation of the accumulator function, or initVal if supplied.
- currVal
The current element being processed in the passed array.
- index
The index of the current element being processed in the passed array.
- array
The passed array
reduce()
was called upon.
- prevVal
- initVal [optional]
Value to use as the first argument to the first call of the accumulator function.
Examples
var f = weaver.fabric();
f.pass([ 1, 2, 3, 4 ]).reduce(function( prev, curr ){
return prev + curr;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Apply a function against an accumulator and each value of the passed array (from right-to-left) has to reduce it to a single value. The function specifies the resultant value via resolve()
or a return value.
- function( prevVal, currVal, index, array )
The accumulator function.
- prevVal
The value previously returned in the last invocation of the accumulator function, or initVal if supplied.
- currVal
The current element being processed in the passed array.
- index
The index of the current element being processed in the passed array.
- array
The passed array
reduceRight()
was called upon.
- prevVal
- initVal [optional]
Value to use as the first argument to the first call of the accumulator function.
Examples
var f = weaver.fabric();
f.pass([ 1, 2, 3, 4 ]).reduceRight(function( prev, curr ){
return prev - curr;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
Sort the passed array, using a divide and conquer strategy with the threads.
- function( a, b ) [optional]
The sorting comparison function that returns a negative value for
a
beforeb
, zero fora
the same asb
, and a positive value fora
afterb
.- a
The first value to compare.
- b
The second value to compare.
- a
Examples
var f = weaver.fabric();
f.pass([ 4, 1, 2, 5, 3 ]).sort().then(function( res ){
console.log('res is ' + res);
f.stop();
});
Split the passed data into slices to spread the data equally among the threads of the fabric. The passed processing function specifies the resultant array slice via a return value or resolve()
.
- function( slice )
The processing function which is run on each slice of passed data.
- slice
An array containing a slice of the passed data.
- slice
Examples
var f = weaver.fabric();
f.pass([ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]).spread(function( slice ){
var res = [];
for( var i = 0; i < slice.length; i++ ){
res.push( slice[i] * slice[i] );
}
return res;
}).then(function( res ){
console.log('res is ' + res);
f.stop();
});
fabric.halt()
, fabric.terminate()
,Terminates the fabric such that all its threads no longer run.
Examples
var f = weaver.fabric();
f.run(function(){
return 2 + 2;
}).then(function( sum ){
console.log('sum should be ' + sum + ' unless the year is 1984');
f.stop();
});
Events
Send a random thread in the fabric a message.
- msg
The message (e.g. JSON object or string) to send to the thread.
Send all threads in the fabric a message.
- msg
The message (e.g. JSON object or string) to send to the threads.
fabric.bind()
, fabric.listen()
, fabric.addListener()
,Bind to events that are emitted by the fabric.
- events
A space separated list of event names.
- data [optional]
A plain object which is passed to the handler in the event object argument.
- function(evt)
The handler function that is called when one of the specified events occurs.
- evt
The event object.
- evt
- eventsMap
A map of event names to handler functions.
- data [optional]
A plain object which is passed to the handler in the event object argument.
fabric.pon()
,Get a promise that is resolved when the first of any of the specified events is triggered on the fabric.
- events
A space separated list of event names.
Bind to events that are emitted by the fabric, and trigger the handler only once.
- events
A space separated list of event names.
- data [optional]
A plain object which is passed to the handler in the event object argument.
- function(evt)
The handler function that is called when one of the specified events occurs.
- evt
The event object.
- evt
- eventsMap
A map of event names to handler functions.
- data [optional]
A plain object which is passed to the handler in the event object argument.
fabric.unbind()
, fabric.unlisten()
, fabric.removeListener()
,Remove event handlers on the fabric.
fabric.emit()
,Trigger one or more events on the fabric.
- events
A space separated list of event names to trigger.
- extraParams [optional]
An array of additional parameters to pass to the handler.