UNPKG

11.5 kBMarkdownView Raw
1# threads.js
2[![Build Status](https://travis-ci.org/andywer/threads.js.svg?branch=master)](https://travis-ci.org/andywer/threads.js)
3[![Coverage Status](https://coveralls.io/repos/github/andywer/threads.js/badge.svg?branch=master)](https://coveralls.io/github/andywer/threads.js?branch=master)
4[![Code Climate](https://codeclimate.com/github/andywer/threads.js/badges/gpa.svg)](https://codeclimate.com/github/andywer/threads.js)
5[![NPM Version](https://img.shields.io/npm/v/threads.svg)](https://www.npmjs.com/package/threads)
6
7Javascript thread library. Uses web workers when run in browsers and child processes
8when run by node.js. Also supports browsers which do not support web workers.
9
10- For client and server use
11- Use different APIs (web worker, node child_process) transparently
12- Thread pools
13- Built-in error handling
14- Well tested
15- ES6 and backwards-compatible
16
17Side note: Using threads.js? Check out the [Version 1.0 API draft](https://github.com/andywer/threads.js/issues/88) and feel free to leave feedback!
18
19
20## Basic usage
21
22Spawn threads to do the time-consuming work and let the parent thread focus on
23daily business!
24
25```javascript
26const spawn = require('threads').spawn;
27
28const thread = spawn(function(input, done) {
29 // Everything we do here will be run in parallel in another execution context.
30 // Remember that this function will be executed in the thread's context,
31 // so you cannot reference any value of the surrounding code.
32 done({ string : input.string, integer : parseInt(input.string) });
33});
34
35thread
36 .send({ string : '123' })
37 // The handlers come here: (none of them is mandatory)
38 .on('message', function(response) {
39 console.log('123 * 2 = ', response.integer * 2);
40 thread.kill();
41 })
42 .on('error', function(error) {
43 console.error('Worker errored:', error);
44 })
45 .on('exit', function() {
46 console.log('Worker has been terminated.');
47 });
48```
49
50
51## Installation
52
53### NPM (Node.js, Browserify, Webpack)
54
55```bash
56npm install --save threads
57```
58
59### Bower
60
61```bash
62bower install --save threads
63```
64
65### Script tag
66
67```html
68<script src="https://unpkg.com/threads@VERSION/dist/threads.browser.min.js"></script>
69```
70
71Note: Replace `VERSION` with the library's version you want to use, like `0.12.0`. The library will be exposed on the global window scope as `threads`.
72
73
74## How To
75
76### Thread code in separate files
77
78You don't have to write the thread's code inline. The file is expected to be a
79commonjs module (so something that uses `module.exports = ...`), for node and
80browser.
81
82```javascript
83const threads = require('threads');
84const config = threads.config;
85const spawn = threads.spawn;
86
87// Set base paths to thread scripts
88config.set({
89 basepath : {
90 node : __dirname + '/../thread-scripts',
91 web : 'http://myserver.local/thread-scripts'
92 }
93});
94
95const thread = spawn('worker.js');
96
97thread
98 .send({ do : 'Something awesome!' })
99 .on('message', function(message) {
100 console.log('worker.js replied:', message);
101 });
102```
103
104worker.js:
105```javascript
106// Use CommonJS syntax (module.exports). Works in browser, too!
107// Only limitation: You won't have require() when run in the browser.
108module.exports = function(input, done) {
109 done('Awesome thread script may run in browser and node.js!');
110};
111```
112
113### Async functions
114
115You can also pass async functions, a.k.a. functions returning a Promise, to spawn threads.
116
117```javascript
118const spawn = require('threads').spawn;
119
120const thread = spawn(function ([a, b]) {
121 // Remember that this function will be run in another execution context.
122 return new Promise(resolve => {
123 setTimeout(() => resolve(a + b), 1000)
124 })
125});
126
127thread
128 .send([ 9, 12 ])
129 // The handlers come here: (none of them is mandatory)
130 .on('message', function(response) {
131 console.log('9 + 12 = ', response);
132 thread.kill();
133 });
134```
135
136
137### Thread Pool
138
139You can also create a thread pool that spawns a fixed no. of workers. Pass jobs
140to the thread pool which it will queue and pass to the next idle worker.
141You can also pass the number threads to be spawned. Defaults to the number of
142CPU cores.
143
144```javascript
145const Pool = require('threads').Pool;
146
147const pool = new Pool();
148// Alternatively: new Pool(<number of threads to spawn>)
149
150// Run a script
151const jobA = pool
152 .run('/path/to/worker')
153 .send({ do : 'something' });
154
155// Run the same script, but with a different parameter
156const jobB = pool
157 .send({ do : 'something else' });
158
159// Run inline code
160const jobC = pool.run(
161 function(input, done) {
162 const hash = md5(input);
163 done(hash, input);
164 }, {
165 // dependencies; resolved using node's require() or the web workers importScript()
166 md5 : 'js-md5'
167 }
168).send('Hash this string!');
169
170jobC
171 .on('done', function(hash, input) {
172 console.log(`Job C hashed: md5("${input}") = "${hash}"`);
173 });
174
175pool
176 .on('done', function(job, message) {
177 console.log('Job done:', job);
178 })
179 .on('error', function(job, error) {
180 console.error('Job errored:', job);
181 })
182 .on('finished', function() {
183 console.log('Everything done, shutting down the thread pool.');
184 pool.killAll();
185 });
186```
187
188#### Job Abortion
189
190You can abort a job by calling `job.abort()`.
191
192```javascript
193const Pool = require('threads').Pool;
194
195const pool = new Pool();
196
197const job = pool
198 .run('/path/to/worker')
199 .send({ do : 'something' });
200
201job.on('abort', () => { console.log('Job Aborted'); });
202
203// somewhere else
204job.abort();
205```
206
207### Streaming
208
209You can also spawn a thread for streaming purposes. The following example shows
210a very simple use case where you keep feeding numbers to the background task
211and it will return the minimum and maximum of all values you ever passed.
212
213```javascript
214const threads = require('threads');
215const spawn = threads.spawn;
216const thread = spawn(function() {});
217
218thread
219 .run(function minmax(int, done) {
220 if (typeof this.min === 'undefined') {
221 this.min = int;
222 this.max = int;
223 } else {
224 this.min = Math.min(this.min, int);
225 this.max = Math.max(this.max, int);
226 }
227 done({ min : this.min, max : this.max }});
228 })
229 .send(2)
230 .send(3)
231 .send(4)
232 .send(1)
233 .send(5)
234 .on('message', function(minmax) {
235 console.log('min:', minmax.min, ', max:', minmax.max);
236 })
237 .on('done', function() {
238 thread.kill();
239 });
240```
241
242### Retraining
243
244As it turns out, `thread.run()` is no one-way road.
245
246```javascript
247thread
248 .run(function doThis(input, done) {
249 done('My first job!');
250 })
251 .send()
252 .run(function doThat(input, done) {
253 done('Old job was boring. Trying something new!');
254 })
255 .send();
256```
257
258### Promises
259
260Instead of using callbacks, you can also turn thread messages and pool jobs into
261promises.
262
263```javascript
264spawn(myThreadFile)
265 .send({ important : 'data' })
266 .promise()
267 .then(function success(message) {}, function error(error) {});
268```
269
270```javascript
271pool.run(fancyThreadCode);
272
273Promise.all([
274 pool.send({ data : 1 }).promise(),
275 pool.send({ data : 2 }).promise()
276]).then(function allResolved() {
277 console.log('Everything done! It\'s closing time...');
278});
279```
280
281### Transferable objects
282
283You can also use transferable objects to improve performance when passing large
284buffers (in browser). Add script files you want to run using importScripts()
285(if in browser) as second parameter to thread.run().
286See [Transferable Objects: Lightning Fast!](http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast).
287
288Both features will be ignored by node.js version for now.
289
290```javascript
291const threads = require('threads');
292const spawn = threads.spawn;
293const thread = spawn(function() {});
294
295const largeArrayBuffer = new Uint8Array(1024 * 1024 * 32); // 32MB
296const jobData = { label : 'huge thing', data: largeArrayBuffer.buffer };
297
298thread
299 .run(function(input, done) {
300 // do something cool with input.label, input.data
301 // call done.transfer() if you want to use transferables in the thread's response
302 // (the node.js code simply ignores the transferables)
303 done.transfer({ some : { response : input.buffer } }, [input.data.buffer]);
304 }, [
305 // this file will be run in the thread using importScripts() if in browser
306 // the node.js code will ignore this second parameter
307 '/dependencies-bundle.js'
308 ])
309 // pass the buffers to transfer into thread context as 2nd parameter to send()
310 .send(jobData, [ largeArrayBuffer.buffer ]);
311```
312
313### Progress update
314
315The thread can also notify the main thread about its current progress.
316
317```javascript
318const threads = require('threads');
319const spawn = threads.spawn;
320const thread = spawn(function() {});
321
322thread
323 .run(function(input, done, progress) {
324 setTimeout(done, 1000);
325 setTimeout(function() { progress(25); }, 250);
326 setTimeout(function() { progress(50); }, 500);
327 setTimeout(function() { progress(75); }, 750);
328 })
329 .send()
330 .on('progress', function(progress) {
331 console.log(`Progress: ${progress}%`);
332 })
333 .on('done', function() {
334 console.log(`Done.`);
335 thread.kill();
336 });
337```
338
339Output:
340
341```
342Progress: 25%
343Progress: 50%
344Progress: 75%
345Done.
346```
347
348### Web worker fallback
349
350You can provide a fallback if the user's browser does not support web workers.
351See [webworker-fallback](https://github.com/andywer/webworker-fallback). This will not have any effect if used by node.js code.
352
353### Debugging threads
354
355When the main process uses `--inspect` to debug Node.js, each thread will be started with the `--inspect` flag too, but
356in a different port so they don't interfere with the main process. Each created thread will have an incremental port, so
357you can create and debug as many as you want.
358
359This also works with `--inspect-brk`. As expected, each thread will pause on the first line when created.
360
361All other flags are passed to the thread unchanged. To override this behaviour, you can pass your own `execArgv` array
362when creating a thread:
363
364```javascript
365// Always open an inspect port on 1234, no matter what the main process is doing.
366spawn(myThreadFile, { execArgv: ['--inspect=1234'] })
367
368// Pass this flag to the thread. Ignore any other flag provided by the main process.
369spawn(myThreadFile, { execArgv: ['--throw-deprecation'] })
370```
371
372### Use external dependencies
373
374Not yet completely implemented.
375
376To do:
377- gulp task to bundle dependencies using browserify and expose all of them -> dependency bundle
378- dependency bundle can be imported by importScripts()
379- code can just call `var myDependency = require('my-dependency');`, no matter if browser or node.js
380
381
382## Configuration
383
384```javascript
385const config = require('threads').config;
386
387// These configuration properties are all optional
388config.set({
389 basepath : {
390 node : 'path/to/my/worker/scripts/directory',
391 web : 'path-or-url/to/my/worker/scripts/directory'
392 },
393 fallback : {
394 slaveScriptUrl : 'path-or-url/to/dist/slave.js' // used for IE where you cannot pass code to a worker using a data URI
395 }
396});
397```
398
399
400## FAQ: Frequently Asked Questions
401
402#### Node: `require()`-ing relative paths in worker does not work (`Error: Cannot find module`)
403Thank you, https://github.com/FlorianBruckner, for reporting the issues and helping to debug them!
404
405**Solution**: Pass down `__dirname` to worker and use it in `require()` (see [Issue 28](https://github.com/andywer/threads.js/issues/28#issuecomment-248505917))
406
407
408## Change log
409
410See [CHANGELOG.md](./CHANGELOG.md).
411
412
413## License
414
415This library is published under the MIT license. See [LICENSE](./LICENSE) for details.
416
417
418__Have fun and build something awesome!__