Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Easy to use, yet powerful multi-threading library for node.js and the browser!
The 'threads' npm package provides a simple and efficient way to create and manage threads (or worker threads) in Node.js. It allows you to run JavaScript code in parallel, taking advantage of multi-core processors to improve performance for CPU-intensive tasks.
Creating a Worker
This feature allows you to create a new worker thread by spawning a worker from a separate file ('./worker'). The worker can then execute functions in parallel to the main thread.
const { spawn, Thread, Worker } = require('threads');
(async () => {
const worker = await spawn(new Worker('./worker'));
console.log(await worker.someFunction());
await Thread.terminate(worker);
})();
Communicating with Workers
This feature demonstrates how to send data to a worker and receive results back. The worker can process the data and return the result to the main thread.
const { spawn, Thread, Worker } = require('threads');
(async () => {
const worker = await spawn(new Worker('./worker'));
const result = await worker.someFunction('data');
console.log(result);
await Thread.terminate(worker);
})();
Error Handling in Workers
This feature shows how to handle errors that occur within worker threads. By using try-catch blocks, you can catch and handle errors gracefully.
const { spawn, Thread, Worker } = require('threads');
(async () => {
try {
const worker = await spawn(new Worker('./worker'));
await worker.someFunctionThatMightFail();
} catch (error) {
console.error('Worker error:', error);
} finally {
await Thread.terminate(worker);
}
})();
'jest-worker' is a package developed by Facebook for parallelizing tasks in Node.js. It is commonly used in the Jest testing framework to run tests in parallel. While it provides similar functionality to 'threads', it is more specialized for use cases involving task parallelization in testing environments.
'piscina' is a fast, efficient worker thread pool implementation for Node.js. It allows you to manage a pool of worker threads to handle multiple tasks concurrently. 'piscina' is designed for high performance and scalability, making it a good alternative to 'threads' for more complex parallel processing needs.
Javascript thread library. Uses web workers when run in browsers and child processes when run by node.js. Also supports browsers which do not support web workers.
Spawn threads to do the time-consuming work and let the parent thread focus on daily business!
const spawn = require('threads').spawn;
const thread = spawn(function(input, done) {
// Everything we do here will be run in parallel in another execution context.
// Remember that this function will be executed in the thread's context,
// so you cannot reference any value of the surrounding code.
done({ string : input.string, integer : parseInt(input.string) });
});
thread
.send({ string : '123' })
// The handlers come here: (none of them is mandatory)
.on('message', function(response) {
console.log('123 * 2 = ', response.integer * 2);
thread.kill();
})
.on('error', function(error) {
console.error('Worker errored:', error);
})
.on('exit', function() {
console.log('Worker has been terminated.');
});
npm install --save threads
bower install --save threads
<script src="https://unpkg.com/threads@VERSION/dist/threads.browser.min.js"></script>
(where VERSION
is the library's version to use, like v0.7.3
)
You don't have to write the thread's code inline. The file is expected to be a
commonjs module (so something that uses module.exports = ...
), for node and
browser.
const threads = require('threads');
const config = threads.config;
const spawn = threads.spawn;
// Set base paths to thread scripts
config.set({
basepath : {
node : __dirname + '/../thread-scripts',
web : 'http://myserver.local/thread-scripts'
}
});
const thread = spawn('worker.js');
thread
.send({ do : 'Something awesome!' })
.on('message', function(message) {
console.log('worker.js replied:', message);
});
worker.js:
// Use CommonJS syntax (module.exports). Works in browser, too!
// Only limitation: You won't have require() when run in the browser.
module.exports = function(input, done) {
done('Awesome thread script may run in browser and node.js!');
};
You can also pass async functions, a.k.a. functions returning a Promise, to spawn threads.
const spawn = require('threads').spawn;
const thread = spawn(function ([a, b]) {
// Remember that this function will be run in another execution context.
return new Promise(resolve => {
setTimeout(() => resolve(a + b), 1000)
})
});
thread
.send([ 9, 12 ])
// The handlers come here: (none of them is mandatory)
.on('message', function(response) {
console.log('9 + 12 = ', response);
thread.kill();
});
You can also create a thread pool that spawns a fixed no. of workers. Pass jobs to the thread pool which it will queue and pass to the next idle worker. You can also pass the number threads to be spawned. Defaults to the number of CPU cores.
const Pool = require('threads').Pool;
const pool = new Pool();
// Alternatively: new Pool(<number of threads to spawn>)
// Run a script
const jobA = pool
.run('/path/to/worker')
.send({ do : 'something' });
// Run the same script, but with a different parameter
const jobB = pool
.send({ do : 'something else' });
// Run inline code
const jobC = pool.run(
function(input, done) {
const hash = md5(input);
done(hash, input);
}, {
// dependencies; resolved using node's require() or the web workers importScript()
md5 : 'js-md5'
}
).send('Hash this string!');
jobC
.on('done', function(hash, input) {
console.log(`Job C hashed: md5("${input}") = "${hash}"`);
});
pool
.on('done', function(job, message) {
console.log('Job done:', job);
})
.on('error', function(job, error) {
console.error('Job errored:', job);
})
.on('finished', function() {
console.log('Everything done, shutting down the thread pool.');
pool.killAll();
});
You can abort a job by calling job.abort()
.
const Pool = require('threads').Pool;
const pool = new Pool();
const job = pool
.run('/path/to/worker')
.send({ do : 'something' });
job.on('abort', () => { console.log('Job Aborted'); });
// somewhere else
job.abort();
You can also spawn a thread for streaming purposes. The following example shows a very simple use case where you keep feeding numbers to the background task and it will return the minimum and maximum of all values you ever passed.
const threads = require('threads');
const spawn = threads.spawn;
const thread = spawn(function() {});
thread
.run(function minmax(int, done) {
if (typeof this.min === 'undefined') {
this.min = int;
this.max = int;
} else {
this.min = Math.min(this.min, int);
this.max = Math.max(this.max, int);
}
done({ min : this.min, max : this.max }});
})
.send(2)
.send(3)
.send(4)
.send(1)
.send(5)
.on('message', function(minmax) {
console.log('min:', minmax.min, ', max:', minmax.max);
})
.on('done', function() {
thread.kill();
});
As it turns out, thread.run()
is no one-way road.
thread
.run(function doThis(input, done) {
done('My first job!');
})
.send()
.run(function doThat(input, done) {
done('Old job was boring. Trying something new!');
})
.send();
Instead of using callbacks, you can also turn thread messages and pool jobs into promises.
spawn(myThreadFile)
.send({ important : 'data' })
.promise()
.then(function success(message) {}, function error(error) {});
pool.run(fancyThreadCode);
Promise.all([
pool.send({ data : 1 }).promise(),
pool.send({ data : 2 }).promise()
]).then(function allResolved() {
console.log('Everything done! It\'s closing time...');
});
You can also use transferable objects to improve performance when passing large buffers (in browser). Add script files you want to run using importScripts() (if in browser) as second parameter to thread.run(). See Transferable Objects: Lightning Fast!.
Both features will be ignored by node.js version for now.
const threads = require('threads');
const spawn = threads.spawn;
const thread = spawn(function() {});
const largeArrayBuffer = new Uint8Array(1024 * 1024 * 32); // 32MB
const jobData = { label : 'huge thing', data: largeArrayBuffer.buffer };
thread
.run(function(input, done) {
// do something cool with input.label, input.data
// call done.transfer() if you want to use transferables in the thread's response
// (the node.js code simply ignores the transferables)
done.transfer({ some : { response : input.buffer } }, [input.data.buffer]);
}, [
// this file will be run in the thread using importScripts() if in browser
// the node.js code will ignore this second parameter
'/dependencies-bundle.js'
])
// pass the buffers to transfer into thread context as 2nd parameter to send()
.send(jobData, [ largeArrayBuffer.buffer ]);
The thread can also notify the main thread about its current progress.
const threads = require('threads');
const spawn = threads.spawn;
const thread = spawn(function() {});
thread
.run(function(input, done, progress) {
setTimeout(done, 1000);
setTimeout(function() { progress(25); }, 250);
setTimeout(function() { progress(50); }, 500);
setTimeout(function() { progress(75); }, 750);
})
.send()
.on('progress', function(progress) {
console.log(`Progress: ${progress}%`);
})
.on('done', function() {
console.log(`Done.`);
thread.kill();
});
Output:
Progress: 25%
Progress: 50%
Progress: 75%
Done.
You can provide a fallback if the user's browser does not support web workers. See webworker-fallback. This will not have any effect if used by node.js code.
When the main process uses --inspect
to debug Node.js, each thread will be started with the --inspect
flag too, but
in a different port so they don't interfere with the main process. Each created thread will have an incremental port, so
you can create and debug as many as you want.
This also works with --inspect-brk
. As expected, each thread will pause on the first line when created.
All other flags are passed to the thread unchanged. To override this behaviour, you can pass your own execArgv
array
when creating a thread:
// Always open an inspect port on 1234, no matter what the main process is doing.
spawn(myThreadFile, { execArgv: ['--inspect=1234'] })
// Pass this flag to the thread. Ignore any other flag provided by the main process.
spawn(myThreadFile, { execArgv: ['--throw-deprecation'] })
Not yet completely implemented.
To do:
var myDependency = require('my-dependency');
, no matter if browser or node.jsconst config = require('threads').config;
// These configuration properties are all optional
config.set({
basepath : {
node : 'path/to/my/worker/scripts/directory',
web : 'path-or-url/to/my/worker/scripts/directory'
},
fallback : {
slaveScriptUrl : 'path-or-url/to/dist/slave.js' // used for IE where you cannot pass code to a worker using a data URI
}
});
require()
-ing relative paths in worker does not work (Error: Cannot find module
)Thank you, https://github.com/FlorianBruckner, for reporting the issues and helping to debug them!
Solution: Pass down __dirname
to worker and use it in require()
(see Issue 28)
See CHANGELOG.md.
This library is published under the MIT license. See LICENSE for details.
Have fun and build something awesome!
FAQs
Web workers & worker threads as simple as a function call
The npm package threads receives a total of 23,870 weekly downloads. As such, threads popularity was classified as popular.
We found that threads demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.