1 | <h1 align="center">
|
2 | <img alt="threads.js" src="./docs/assets/logo-label.png" width="75%" />
|
3 | </h1>
|
4 | <p align="center">
|
5 | <a href="https://travis-ci.org/andywer/threads.js" target="_blank"><img alt="Build status" src="https://img.shields.io/travis/andywer/threads.js/v1.svg?style=flat-square"></a>
|
6 | <a href="https://www.npmjs.com/package/threads" target="_blank"><img alt="npm (tag)" src="https://img.shields.io/npm/v/threads.svg?style=flat-square"></a>
|
7 | </p>
|
8 |
|
9 | <br />
|
10 |
|
11 | Offload CPU-intensive tasks to worker threads in node.js, web browsers and electron using one uniform API.
|
12 |
|
13 | Uses web workers in the browser, `worker_threads` in node 12+ and [`tiny-worker`](https://github.com/avoidwork/tiny-worker) in node 8 to 11.
|
14 |
|
15 | ### Features
|
16 |
|
17 | * First-class support for **async functions** & **observables**
|
18 | * Write code once, run it **on all platforms**
|
19 | * Manage bulk task executions with **thread pools**
|
20 | * Use **require()** and **import**/**export** in workers
|
21 | * Works great with **webpack**
|
22 |
|
23 | ### Version 0.x
|
24 |
|
25 | You can find the old version 0.12 of threads.js on the [`v0` branch](https://github.com/andywer/threads.js/tree/v0). All the content on this page refers to version 1.0 which is a rewrite of the library with a whole new API.
|
26 |
|
27 | ## Installation
|
28 |
|
29 | ```
|
30 | npm install threads tiny-worker
|
31 | ```
|
32 |
|
33 | *You only need to install the `tiny-worker` package to support node.js < 12. It's an optional dependency and used as a fallback if `worker_threads` are not available.*
|
34 |
|
35 | ## Platform support
|
36 |
|
37 | <details>
|
38 | <summary>Run on node.js</summary>
|
39 |
|
40 | <p></p>
|
41 |
|
42 | Running code using threads.js in node works out of the box.
|
43 |
|
44 | Note that we wrap the native `Worker`, so `new Worker("./foo/bar")` will resolve the path relative to the module that calls it, not relative to the current working directory.
|
45 |
|
46 | That aligns it with the behavior when bundling the code with webpack or parcel.
|
47 |
|
48 | </details>
|
49 |
|
50 | <details>
|
51 | <summary>Webpack build setup</summary>
|
52 |
|
53 | #### Webpack config
|
54 |
|
55 | Use with the [`threads-plugin`](https://github.com/andywer/threads-plugin). It will transparently detect all `new Worker("./unbundled-path")` expressions, bundles the worker code and replaces the `new Worker(...)` path with the worker bundle path, so you don't need to explicitly use the `worker-loader` or define extra entry points.
|
56 |
|
57 | ```sh
|
58 | npm install -D threads-plugin
|
59 | ```
|
60 |
|
61 | Then add it to your `webpack.config.js`:
|
62 |
|
63 | ```diff
|
64 | + const ThreadsPlugin = require('threads-plugin');
|
65 |
|
66 | module.exports = {
|
67 | // ...
|
68 | plugins: [
|
69 | + new ThreadsPlugin()
|
70 | ]
|
71 | // ...
|
72 | }
|
73 | ```
|
74 |
|
75 | #### Node.js bundles
|
76 |
|
77 | If you are using webpack to create a bundle that will be run in node (webpack config `target: "node"`), you also need to specify that the `tiny-worker` package used for node < 12 should not be bundled:
|
78 |
|
79 | ```diff
|
80 | module.exports = {
|
81 | // ...
|
82 | + externals: {
|
83 | + "tiny-worker": "tiny-worker"
|
84 | + }
|
85 | // ...
|
86 | }
|
87 | ```
|
88 |
|
89 | Make sure that `tiny-worker` is listed in your `package.json` `dependencies` in that case.
|
90 |
|
91 | #### When using TypeScript
|
92 |
|
93 | Make sure the TypeScript compiler keeps the `import` / `export` statements intact, so webpack resolves them. Otherwise the `threads-plugin` won't be able to do its job.
|
94 |
|
95 | ```diff
|
96 | module.exports = {
|
97 | // ...
|
98 | module: {
|
99 | rules: [
|
100 | {
|
101 | test: /\.ts$/,
|
102 | loader: "ts-loader",
|
103 | + options: {
|
104 | + compilerOptions: {
|
105 | + module: "esnext"
|
106 | + }
|
107 | + }
|
108 | }
|
109 | ]
|
110 | },
|
111 | // ...
|
112 | }
|
113 | ```
|
114 |
|
115 | </details>
|
116 |
|
117 | <details>
|
118 | <summary>Parcel bundler setup</summary>
|
119 |
|
120 | <p></p>
|
121 |
|
122 | You need to import `threads/register` once at the beginning of your application code (in the master code, not in the workers):
|
123 |
|
124 | ```diff
|
125 | import { spawn } from "threads"
|
126 | + import "threads/register"
|
127 |
|
128 | // ...
|
129 |
|
130 | const work = await spawn(new Worker("./worker"))
|
131 | ```
|
132 |
|
133 | This registers the library's `Worker` implementation for your platform as the global `Worker`. This is necessary, since you cannot `import { Worker } from "threads"` or Parcel won't recognize `new Worker()` as a web worker anymore.
|
134 |
|
135 | Be aware that this might affect any code that tries to instantiate a normal web worker `Worker` and now instead instantiates a threads.js `Worker`. The threads.js `Worker` is just a web worker with some sugar on top, but that sugar might have unexpected side effects on third-party libraries.
|
136 |
|
137 | Everything else should work out of the box.
|
138 |
|
139 | </details>
|
140 |
|
141 | ## Getting Started
|
142 |
|
143 | ### Basics
|
144 |
|
145 | ```js
|
146 | // master.js
|
147 | import { spawn, Thread, Worker } from "threads"
|
148 |
|
149 | const auth = await spawn(new Worker("./workers/auth"))
|
150 | const hashed = await auth.hashPassword("Super secret password", "1234")
|
151 |
|
152 | console.log("Hashed password:", hashed)
|
153 |
|
154 | await Thread.terminate(auth)
|
155 | ```
|
156 |
|
157 | ```js
|
158 | // workers/auth.js
|
159 | import sha256 from "js-sha256"
|
160 | import { expose } from "threads/worker"
|
161 |
|
162 | expose({
|
163 | hashPassword(password, salt) {
|
164 | return sha256(password + salt)
|
165 | }
|
166 | })
|
167 | ```
|
168 |
|
169 | ### spawn()
|
170 |
|
171 | The `hashPassword()` function of the `auth` object in the master code proxies the call to the `hashPassword()` function in the worker:
|
172 |
|
173 | If the worker's function returns a promise or an observable then you can just use the return value as such in the master code. If the function returns a primitive value, expect the master function to return a promise resolving to that value.
|
174 |
|
175 | ### expose()
|
176 |
|
177 | Use `expose()` to make a function or an object containing methods callable from the master thread.
|
178 |
|
179 | In case of exposing an object, `spawn()` will asynchronously return an object exposing all the object's functions. If you `expose()` a function, `spawn` will also return a callable function, not an object.
|
180 |
|
181 | ## Usage
|
182 |
|
183 | <p>
|
184 | Find the full documentation on the <a href="https://threads.js.org/" rel="nofollow">website</a>:
|
185 | </p>
|
186 |
|
187 | - [**Quick start**](https://threads.js.org/getting-started)
|
188 | - [**Basic usage**](https://threads.js.org/usage)
|
189 | - [**Using observables**](https://threads.js.org/usage-observables)
|
190 | - [**Thread pools**](https://threads.js.org/usage-pool)
|
191 | - [**Advanced**](https://threads.js.org/usage-advanced)
|
192 |
|
193 | ## Webpack
|
194 |
|
195 | Threads.js works with webpack. Usually all you need to do is adding the
|
196 | [`threads-plugin`](https://github.com/andywer/threads-plugin).
|
197 |
|
198 | See [Build with webpack](https://threads.js.org/getting-started#build-with-webpack)
|
199 | on the website for details.
|
200 |
|
201 |
|
202 | ## API
|
203 |
|
204 | TODO
|
205 | -->
|
206 |
|
207 | ## Debug
|
208 |
|
209 | We are using the [`debug`](https://github.com/visionmedia/debug) package to provide opt-in debug logging. All the package's debug messages have a scope starting with `threads:`, with different sub-scopes:
|
210 |
|
211 | - `threads:master:messages`
|
212 | - `threads:master:spawn`
|
213 | - `threads:master:thread-utils`
|
214 | - `threads:pool:${poolName || poolID}`
|
215 |
|
216 | Set it to `DEBUG=threads:*` to enable all the library's debug logging. To run its tests with full debug logging, for instance:
|
217 |
|
218 | ```
|
219 | DEBUG=threads:* npm test
|
220 | ```
|
221 |
|
222 | ## License
|
223 |
|
224 | MIT
|