Name: workerpool
Owner: Formidable
Description: Offload tasks to a pool of workers on node.js and in the browser
Forked from: josdejong/workerpool
Created: 2017-04-06 03:55:43.0
Updated: 2017-04-06 03:55:45.0
Pushed: 2017-04-06 18:39:14.0
Size: 313
Language: JavaScript
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
JavaScript is based upon a single event loop which handles one event at a time. Jeremy Epstein explains this clearly:
In Node.js everything runs in parallel, except your code. What this means is that all I/O code that you write in Node.js is non-blocking, while (conversely) all non-I/O code that you write in Node.js is blocking.
This means that CPU heavy tasks will block other tasks from being executed. In case of a browser environment, the browser will not react to user events like a mouse click while executing a CPU intensive task (the browser “hangs”). In case of a node.js server, the server will not respond to any new request while executing a single, heavy request.
For front-end processes, this is not a desired situation. Therefore, CPU intensive tasks should be offloaded from the main event loop onto dedicated workers. In a browser environment, Web Workers can be used. In node.js, child processes are available. An application should be split in separate, decoupled parts, which can run independent of each other in a parallelized way. Effectively, this results in an architecture which achieves concurrency by means of isolated processes and message passing.
workerpool offers an easy way to create a pool of workers for both dynamically offloading computations as well as managing a pool of dedicated workers. workerpool basically implements a thread pool pattern. There is a pool of workers to execute tasks. New tasks are put in a queue. A worker executes one task at a time, and once finished, picks a new task from the queue. Workers can be accessed via a natural, promise based proxy, as if they are available straight in the main application.
workerpool runs on node.js, Chrome, Firefox, Opera, Safari, and IE10+.
Install via npm:
npm install workerpool
To load workerpool in a node.js application (both main application as well as workers):
workerpool = require('workerpool');
To load workerpool in the browser:
ipt src="workerpool.js"></script>
To load workerpool in a web worker in the browser:
rtScripts('workerpool.js');
In the following example there is a function add
, which is offloaded dynamically to a worker to be executed for a given set of arguments.
myApp.js
workerpool = require('workerpool');
pool = workerpool.pool();
tion add(a, b) {
turn a + b;
.exec(add, [3, 4])
.then(function (result) {
console.log('result', result); // outputs 7
pool.clear(); // clear all workers when done
});
Note that both function and arguments must be static and stringifiable, as they need to be send to the worker in a serialized form. In case of large functions or function arguments, the overhead of sending the data to the worker can be significant.
A dedicated worker can be created in a separate script, and then used via a worker pool.
myWorker.js
workerpool = require('workerpool');
deliberately inefficient implementation of the fibonacci sequence
tion fibonacci(n) {
(n < 2) return n;
turn fibonacci(n - 2) + fibonacci(n - 1);
reate a worker and register public functions
erpool.worker({
bonacci: fibonacci
This worker can be used by a worker pool:
myApp.js
workerpool = require('workerpool');
reate a worker pool using an external worker script
pool = workerpool.pool(__dirname + '/myWorker.js');
un registered functions on the worker via exec
.exec('fibonacci', [10])
.then(function (result) {
console.log('Result: ' + result); // outputs 55
pool.clear(); // clear all workers when done
});
r run registered functions on the worker via a proxy:
.proxy()
.then(function (worker) {
worker.fibonacci(10)
.then(function (result) {
console.log('Result: ' + result); // outputs 55
});
});
Worker can also initialize asynchronously:
myAsyncWorker.js
ne(['workerpool/dist/workerpool'], function(workerpool) {
a deliberately inefficient implementation of the fibonacci sequence
nction fibonacci(n) {
if (n < 2) return n;
return fibonacci(n - 2) + fibonacci(n - 1);
create a worker and register public functions
rkerpool.worker({
fibonacci: fibonacci
;
Examples are available in the examples directory:
https://github.com/josdejong/workerpool/tree/master/examples
The API of workerpool consists of two parts: a function workerpool.pool
to create a worker pool, and a function workerpool.worker
to create a worker.
A workerpool can be created using the function workerpool.pool
:
workerpool.pool([script: string] [, options: Object]) : Pool
When a script
argument is provided, the provided script will be started as a dedicated worker.
When no script
argument is provided, a default worker is started which can be used to offload functions dynamically via Pool.exec
.
Note that on node.js, script
must be an absolute file path like __dirname + '/myWorker.js'
.
The following options are available:
minWorkers: number | 'max'
. The minimum number of workers that must be initialized and kept available. Setting this to 'max'
will create maxWorkers
default workers (see below).maxWorkers: number
. The default number of maxWorkers is the number of CPU's minus one. When the number of CPU's could not be determined (for example in older browsers), maxWorkers
is set to 3.A worker pool contains the following functions:
Pool.exec(method: Function | string, params: Array | null) : Promise.<*, Error>
Execute a function on a worker with given arguments.
When method
is a string, a method with this name must exist at the worker and must be registered to make it accessible via the pool. The function will be executed on the worker with given parameters.
When method
is a function, the provided function fn
will be stringified, send to the worker, and executed there with the provided parameters. The provided function must be static, it must not depend on variables in a surrounding scope.
Pool.proxy() : Promise.<Object, Error>
Create a proxy for the worker pool. The proxy contains a proxy for all methods available on the worker. All methods return promises resolving the methods result.
Pool.stats() : Object
Retrieve statistics on workers, and active and pending tasks.
Returns an object containing the following properties:
alWorkers: 0,
yWorkers: 0,
eWorkers: 0,
dingTasks: 0,
iveTasks: 0
Pool.clear([force: boolean])
Clear all workers from the pool. If parameter force
is false (default), workers will finish the tasks they are working on before terminating themselves. When force
is true, all workers are terminated immediately without finishing running tasks.
The function Pool.exec
and the proxy functions all return a Promise
. The promise has the following functions available:
Promise.then(fn: Function.<result: *>)
Promise.catch(fn: Function.<error: Error>)
Promise.cancel()
Promise.CancellationError
.Promise.timeout(delay: number)
Promise.TimeoutError
.Example usage:
workerpool = require('workerpool');
tion add(a, b) {
turn a + b;
pool1 = workerpool.pool();
ffload a function to a worker
1.exec(add, [2, 4])
.then(function (result) {
console.log(result); // will output 6
});
reate a dedicated worker
pool2 = workerpool.pool(__dirname + '/myWorker.js');
upposed myWorker.js contains a function 'fibonacci'
2.exec('fibonacci', [10])
.then(function (result) {
console.log(result); // will output 55
});
reate a proxy to myWorker.js
2.proxy()
.then(function (myWorker) {
myWorker.fibonacci(10)
.then(function (result) {
console.log(result); // will output 55
});
});
reate a pool with a specified maximum number of workers
pool3 = workerpool.pool({maxWorkers: 7});
A worker is constructed as:
workerpool.worker([methods: Object.<String, Function>])
Argument methods
is optional can can be an object with functions available in the worker. Registered functions will be available via the worker pool.
Example usage:
ile myWorker.js
workerpool = require('workerpool');
tion add(a, b) {
turn a + b;
tion multiply(a, b) {
turn a * b;
reate a worker and register functions
erpool.worker({
d: add,
ltiply: multiply
Asynchronous results can be handled by returning a Promise from a function in the worker:
ile myWorker.js
workerpool = require('workerpool');
tion timeout(delay) {
turn new Promise(function (resolve, reject) {
setTimeout(resolve, delay)
;
reate a worker and register functions
erpool.worker({
meout: timeout
Following properties are available for convenience:
map
, reduce
, forEach
,
filter
, some
, every
, …First clone the project from github:
git clone git://github.com/josdejong/workerpool.git
cd workerpool
Install the project dependencies:
npm install
Then, the project can be build by executing the build script via npm:
npm run build
This will build the library workerpool.js and workerpool.min.js from the source files and put them in the folder dist.
To execute tests for the library, install the project dependencies once:
npm install
Then, the tests can be executed:
npm test
To test code coverage of the tests:
npm run coverage
To see the coverage results, open the generated report in your browser:
./coverage/lcov-report/index.html
Copyright © 2014-2016 Jos de Jong wjosdejong@gmail.com
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.