easy-web-worker 7.0.5 โ†’ 7.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +943 -327
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,539 +1,1155 @@
1
1
  # easy-web-worker ๐ŸŒŸ
2
2
 
3
- ![Image John Avatar](https://raw.githubusercontent.com/johnny-quesada-developer/global-hooks-example/main/public/avatar2.jpeg)
3
+ <div align="center">
4
4
 
5
- Hello and welcome to **easy-web-worker** with [easy-cancelable-promise](https://www.npmjs.com/package/easy-cancelable-promise) โ€“ your go-to solution for seamless **Web Workers** integration, now enhanced with cancelable promises! ๐Ÿš€
5
+ ![Johnny Quesada](https://raw.githubusercontent.com/johnny-quesada-developer/global-hooks-example/main/public/avatar2.jpeg)
6
6
 
7
- [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) are a native tool provided by **JavaScript**, allowing you to use them within your favorite framework like **React**, **Angular**, **Vue**, and others, or with pure **JavaScript** and **TypeScript**.
7
+ </div>
8
8
 
9
- Check out the running example with **React** and **TypeScript** at [CODEPEN](https://codepen.io/johnnynabetes/full/wvOvygW); let's explore the capabilities of JavaScript's concurrent processing with Web Workers!"
9
+ <div align="center">
10
10
 
11
- [Important!] Starting from version 4.0.0, EasyWebWorker supports concurrency mode. This means you can now configure whether a single **EasyWebWorker** should use multiple Web Worker instances. This feature is extremely powerful for code that requires not only heavy computations occasionally but also robust concurrent processing. For more detailed information, please see the section below. [concurrency mode](#concurrency-mode)
11
+ **Real Web Workers. Simple API. Cancelable work.** ๐Ÿš€
12
12
 
13
- #### IMPORTANT!
13
+ _Heavy computation off the main thread โ€” without fighting the native Worker API._ โœจ
14
14
 
15
- If you were previously using **easy-web-worker** with **cancelable-promise-jq**, please note that the **cancelable-promise-jq** package has been renamed/deprecated. To continue using the latest version of **easy-web-worker**, simply uninstall **cancelable-promise-jq** and replace all imports with **easy-cancelable-promise**.
15
+ [![npm version](https://img.shields.io/npm/v/easy-web-worker.svg)](https://www.npmjs.com/package/easy-web-worker)
16
+ [![Downloads](https://img.shields.io/npm/dm/easy-web-worker.svg)](https://www.npmjs.com/package/easy-web-worker)
17
+ [![License](https://img.shields.io/github/license/johnny-quesada-developer/easy-web-worker)](https://github.com/johnny-quesada-developer/easy-web-worker/blob/main/LICENSE)
16
18
 
17
- I sincerely apologize for any inconvenience this may cause.
19
+ [**Live Demo**](https://johnny-quesada-developer.github.io/easy-web-workers-example/) โ€ข [**Video Tutorial**](https://www.youtube.com/watch?v=CK-Uri9lDOE) โ€ข [**CodePen**](https://codepen.io/johnnynabetes/full/wvOvygW)
18
20
 
19
- ### Creating a web worker never was easier!
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## ๐ŸŽฏ The One-Liner
20
26
 
21
27
  ```ts
22
- import createEasyWebWorker from 'easy-web-worker/createEasyWebWorker';
28
+ import { createEasyWebWorker } from 'easy-web-worker';
29
+
30
+ const worker = createEasyWebWorker<number, number>(({ onMessage }) => {
31
+ onMessage((message) => message.resolve(message.payload * 2));
32
+ });
33
+ ```
34
+
35
+ **That's it.** No separate worker file. No message-ID plumbing. No manual promise bridge. ๐Ÿงต
36
+
37
+ ```ts
38
+ const result = await worker.send(21);
39
+
40
+ console.log(result); // 42
41
+ ```
42
+
43
+ Your code runs inside a **real native Web Worker**, while the main thread receives a `CancelablePromise`.
44
+
45
+ ---
46
+
47
+ ## ๐Ÿš€ Why Developers Love This Library
23
48
 
24
- /**
25
- * The callback parameter will be the body of the worker
26
- */
49
+ ### ๐ŸŽ“ **Small API Surface**
50
+
51
+ If you already understand native Web Workers, the mental model stays familiar:
52
+
53
+ ```ts
27
54
  const worker = createEasyWebWorker(({ onMessage }) => {
28
- const fibonacci = (n) => {
55
+ onMessage((message) => {
56
+ // Work inside the Worker
57
+ message.resolve('done');
58
+ });
59
+ });
60
+
61
+ await worker.send();
62
+ ```
63
+
64
+ ### โšก **Real Background Execution**
65
+
66
+ Move CPU-heavy work away from the browser's main thread so rendering and user interaction can stay responsive.
67
+
68
+ ```ts
69
+ const worker = createEasyWebWorker<number, number>(({ onMessage }) => {
70
+ const fibonacci = (n: number): number => {
29
71
  if (n <= 1) return n;
30
72
  return fibonacci(n - 1) + fibonacci(n - 2);
31
73
  };
32
74
 
33
- /**
34
- * Inside the worker we have to define an action when onMessage
35
- */
36
75
  onMessage((message) => {
37
- /**
38
- * The payload includes whatever parameters are sent from the main thread
39
- */
40
- const { payload } = message;
41
- const result = fibonacci(payload.base);
42
-
43
- message.resolve(result);
44
- //message.reject(); // or reject
76
+ message.resolve(fibonacci(message.payload));
45
77
  });
46
78
  });
47
79
  ```
48
80
 
49
- Then, for sending a message to the worker:
81
+ ### ๐Ÿ›‘ **Cancelable Promises**
82
+
83
+ Every `send()` returns a `CancelablePromise` from [`easy-cancelable-promise`](https://www.npmjs.com/package/easy-cancelable-promise).
50
84
 
51
85
  ```ts
52
- /**
53
- * This returns a CancelablePromise
54
- */
55
- await worker.send(40);
86
+ const task = worker.send(45);
87
+
88
+ task.cancel('No longer needed');
56
89
  ```
57
90
 
58
- And that's it! You now have a worker running heavy computation in a real separate thread, with real asynchronous programming in JavaScript.
91
+ Cancellation can travel **from the main thread into the Worker**, allowing the Worker to react and release resources.
59
92
 
60
- You can also create an easy web worker from a static file, or from a native worker instance, here various examples:
93
+ ### ๐Ÿ“Š **Progress Reporting**
94
+
95
+ Long-running jobs can report progress without resolving the request.
61
96
 
62
97
  ```ts
63
- // on vite and typescript
64
- import workerUrl from './worker?worker&url';
98
+ worker.send(payload).onProgress((percentage) => {
99
+ console.log(`${percentage}%`);
100
+ });
101
+ ```
65
102
 
66
- const isProduction = import.meta.env.MODE === 'production';
103
+ ### ๐Ÿšฆ **Built-In Worker Pool**
67
104
 
68
- const workerSource = isProduction
69
- ? workerUrl
70
- : new URL('./worker.ts', import.meta.url);
105
+ Scale a single `EasyWebWorker` across multiple native Worker instances.
71
106
 
72
- const worker = new EasyWebWorker(workerSource, {
73
- workerOptions: {
74
- type: 'module',
75
- },
107
+ ```ts
108
+ const worker = createEasyWebWorker(workerBody, {
109
+ maxWorkers: 4,
76
110
  });
111
+ ```
77
112
 
78
- // or
79
- const worker = createEasyWebWorker(workerSource, {
80
- workerOptions: {
81
- type: 'module',
82
- },
83
- });
113
+ The public API remains the same โ€” the library manages worker creation and message distribution.
84
114
 
85
- // if you are using ESM - ECMAScript Modules
86
- const worker = createEasyWebWorker([
87
- new Worker(new URL('./worker.js', import.meta.url)),
88
- ]);
115
+ ### ๐Ÿงฉ **Multiple Worker Sources**
116
+
117
+ Use whichever architecture fits the project:
118
+
119
+ ```ts
120
+ createEasyWebWorker(workerBody); // Runtime Worker template
121
+ createEasyWebWorker('./worker.js'); // Static Worker file
122
+ createEasyWebWorker(new URL(...)); // URL
123
+ createEasyWebWorker(new Worker(...)); // Existing Worker
124
+ createEasyWebWorker([worker1, worker2]); // Existing Worker pool
125
+ ```
126
+
127
+ ---
89
128
 
90
- // other ways with vanilla js
91
- const worker = createEasyWebWorker('./worker.js');
92
- const worker = createEasyWebWorker(new Worker('./worker.js')); // ฦ’ Worker() { [native code] }
129
+ ## ๐Ÿ“ฆ Installation
93
130
 
94
- const worker = new EasyWebWorker('./worker.js');
95
- const worker = new EasyWebWorker(new Worker('./worker.js')); // ฦ’ Worker() { [native code] }
131
+ ```bash
132
+ npm install easy-web-worker
96
133
  ```
97
134
 
98
- When working with **static files**, which can offer substantial benefits with web workers, you simply need to create an instance of **StaticEasyWebWorker**.
135
+ `easy-web-worker` uses [`easy-cancelable-promise`](https://www.npmjs.com/package/easy-cancelable-promise) for the promise lifecycle.
99
136
 
100
- The **StaticEasyWebWorker** provides an interface to continue working with [easy-cancelable-promise](https://www.npmjs.com/package/easy-cancelable-promise) and build more complex APIs within your worker.
137
+ > **Upgrading from an old release?**
138
+ > If your project still imports `cancelable-promise-jq`, replace it with `easy-cancelable-promise`. The old package name is deprecated.
101
139
 
102
- From inside your worker, the message callbacks receive a message that includes multiple methods and functions. You can use these to communicate back with the main thread, or to subscribe to and react to the lifecycle of a worker.
140
+ ---
141
+
142
+ ## ๐ŸŽฌ Quick Start
143
+
144
+ ### 30 Seconds to a Web Worker
103
145
 
104
146
  ```ts
105
- const { onMessage } = new StaticEasyWebWorker();
147
+ import { createEasyWebWorker } from 'easy-web-worker';
106
148
 
107
- /**
108
- * For adding a default onMessage
109
- */
110
- onMessage((message) => {
111
- /** Your message receives a payload,
112
- * which is any information sent from the main thread.*/
113
- const { payload } = message;
149
+ const fibonacciWorker = createEasyWebWorker<number, number>(({ onMessage }) => {
150
+ const fibonacci = (n: number): number => {
151
+ if (n <= 1) return n;
152
+ return fibonacci(n - 1) + fibonacci(n - 2);
153
+ };
114
154
 
115
- const bigArrayBuffer = new new ArrayBuffer(1000000)();
155
+ onMessage((message) => {
156
+ message.resolve(fibonacci(message.payload));
157
+ });
158
+ });
159
+
160
+ const result = await fibonacciWorker.send(40);
116
161
 
117
- /** You can resolve the message and respond to the main thread's promise that is listening.
118
- * This promise could also send data back to the main thread and transfer data if needed */
119
- message.resolve({ bigArrayBuffer }, [bigArrayBuffer]);
162
+ console.log(result);
163
+ ```
120
164
 
121
- /** You can reject the message and send back a reason,
122
- * if no transfer is necessary just avoid the second parameter */
123
- message.reject(new Error('something happened'));
165
+ The Fibonacci calculation executes in a separate native Worker thread instead of blocking the page's main thread. โšก
124
166
 
125
- const metadata = { message: 'progress from inside the worker' };
167
+ ### 60 Seconds to Production-Ready
126
168
 
127
- /** You can report progress */
128
- message.reportProgress(10, metadata, []); // all the methods allows you to send Transferable[]
169
+ Run [`jsdiff`](https://www.npmjs.com/package/diff) inside a Web Worker so text comparison never blocks the main thread.
129
170
 
130
- /** You can cancel an operation from within the worker */
131
- message.cancel('the operating was canceled from the worker');
171
+ #### ๐ŸŒ Simple jsdiff implementation.
132
172
 
133
- /** You can subscribe to the cancellation event of the message,
134
- * regardless of whether this cancellation is internal or external to the worker.*/
135
- const unsubscribeCancel = message.onCancel((reason) => {});
173
+ No Worker file. No extra bundler configuration. Just give `easy-web-worker` the public script URL:
136
174
 
137
- // you can unsubscribe from the cancel event as well
138
- unsubscribeCancel();
175
+ ```ts
176
+ // example.js
177
+ import { createEasyWebWorker } from 'easy-web-worker';
139
178
 
140
- /**
141
- * And there is extra listeners for the other events
142
- * This events represent the lifecycle of the message
143
- */
144
- const unsubscribeResolve = message.onResolve((data) => {});
145
- const unsubscribeReject = message.onReject((data) => {});
146
- const unsubscribeProgress = message.onProgress((data) => {});
147
- const unsubscribeFinalize = message.onFinalize((data) => {});
148
-
149
- /** You can also review the status of the message at any time*/
150
- const status = message.getStatus(); // pending | resolved | rejected | canceled
179
+ const worker = createEasyWebWorker(
180
+ ({ onMessage }) => {
181
+ onMessage((message, context) => {
182
+ const { input1, input2 } = message.payload;
183
+
184
+ message.resolve(context.Diff.diffWords(input1, input2));
185
+ });
186
+ },
187
+ {
188
+ scripts: ['https://cdn.jsdelivr.net/npm/diff@9.0.0/dist/diff.min.js'],
189
+ }
190
+ );
191
+
192
+ const result = await worker.send({
193
+ input1: 'Web Workers are powerful.',
194
+ input2: 'Web Workers are incredibly powerful.',
151
195
  });
152
196
 
153
- /**
154
- * For adding specific actions
155
- */
156
- onMessage('readCSV', (message) => {
157
- // do something
197
+ console.log(result);
198
+ ```
199
+
200
+ **That's it.** The CDN script is loaded inside the Worker, `jsdiff` exposes its `Diff` API there, and your main thread only sends data and awaits the result. ๐Ÿš€
201
+
202
+ - โœ… No separate Worker file
203
+ - โœ… No npm import for jsdiff in your application bundle
204
+ - โœ… No manual `importScripts()`
205
+ - โœ… No `postMessage` / `onmessage` plumbing
206
+ - โœ… Type-safe request and result
207
+ - โœ… Real background execution
208
+
209
+ #### ๐Ÿ“„ Static Worker file and specific actions
210
+
211
+ If you prefer normal module imports, your own Worker file, or bundler-controlled dependencies, use `StaticEasyWebWorker`.
212
+
213
+ **`TextDiff.worker.ts`**
214
+
215
+ ```ts
216
+ import { diffWords, type Change } from 'diff';
217
+ import { StaticEasyWebWorker } from 'easy-web-worker';
218
+ import type { ComparePayload, Change } from './TextDiff.types';
219
+
220
+ const easyWorker = new StaticEasyWebWorker();
221
+
222
+ // you can define multiple named methods inside the Worker
223
+ easyWorker.onMessage<ComparePayload, Change[]>('compare', (message) => {
224
+ const { input1, input2 } = message.payload;
225
+
226
+ message.resolve(diffWords(input1, input2));
227
+ });
228
+
229
+ easyWorker.onMessage<string, string>('uppercase', (message) => {
230
+ message.resolve(message.payload.toUpperCase());
158
231
  });
159
232
  ```
160
233
 
161
- It's important to mention that the **cancel** method is the only one that provides two-way binding. It can travel all the way from the main thread to the worker, cancel something inside the worker, and notify the main thread upon completion.
234
+ **That's the whole Worker.** Now connect it to your application:
162
235
 
163
- **easy-web-worker** is designed to enhance the capabilities of the **Worker** class by integrating a pattern of cancelable promises from the [easy-cancelable-promise](https://www.npmjs.com/package/easy-cancelable-promise) library. For straightforward tasks, it simplifies the process by eliminating the need to configure webpack or other bundlers. And for more complex requirements, the **StaticEasyWebWorker** class allows the integration of easy worker and cancelable promises capabilities into your static workers.
236
+ ```ts
237
+ import { EasyWebWorker } from 'easy-web-worker';
238
+ import workerUrl from './TextDiff.worker?worker&url';
164
239
 
165
- Start enhancing your applications with robust, cancelable promises and easy web worker integration today! ๐ŸŒ
240
+ // For vite, use the Worker URL in production and the TypeScript file in development.
241
+ const isProduction = import.meta.env.MODE === 'production';
166
242
 
167
- Experience it in action with a [Live Example featuring text-diff](https://johnny-quesada-developer.github.io/easy-web-workers-example/) ๐Ÿ“˜.
243
+ const worker = new EasyWebWorker(
244
+ isProduction ? workerUrl : new URL('./TextDiff.worker.ts', import.meta.url),
245
+ {
246
+ workerOptions: {
247
+ type: 'module',
248
+ },
249
+ }
250
+ );
168
251
 
169
- For a comprehensive understanding, watch our informative [introduction video](https://www.youtube.com/watch?v=CK-Uri9lDOE) ๐ŸŽฅ. You can also dive deeper into the code and explore on [easy-web-workers-examples](https://github.com/johnny-quesada-developer/easy-web-workers-example) ๐Ÿงฉ.
252
+ const result = await worker.sendToMethod<ComparePayload, Change[]>('compare', {
253
+ input1: 'Web Workers are powerful.',
254
+ input2: 'Web Workers are incredibly powerful.',
255
+ });
256
+ ```
170
257
 
171
- ## Creating a simple Web Worker
258
+ Both approaches give you the same core result: a normal JavaScript library doing CPU work inside a **Web Worker**, while your application talks to it through a Promise-based API.
172
259
 
173
- Creating a new worker is as simple as
260
+ | Approach | Best for |
261
+ | ----------------------- | ---------------------------------------------------------------------------- |
262
+ | ๐ŸŒ CDN + runtime Worker | Fastest setup, demos, small integrations, no Worker file |
263
+ | ๐Ÿ“„ Static Worker | Module imports, larger Worker code, bundler control, production architecture |
174
264
 
175
- ```TS
176
- const backgroundWorker = createEasyWebWorker<string, string>(({ onMessage }) => {
177
- onMessage((message) => {
178
- const { payload } = message;
265
+ And in both cases you still get the `easy-web-worker` features around the Worker:
266
+
267
+ - โœ… Promise-based communication
268
+ - โœ… Cancellation
269
+ - โœ… Progress reporting
270
+ - โœ… Named Worker methods
271
+ - โœ… Worker pooling when needed
272
+
273
+ ---
274
+
275
+ ## ๐ŸŒŸ Core Features Deep Dive
276
+
277
+ ### 1๏ธโƒฃ Runtime Workers with `createEasyWebWorker`
278
+
279
+ Create a Worker directly from a function template.
280
+
281
+ #### ๐ŸŽจ The Basics
282
+
283
+ ```ts
284
+ import { createEasyWebWorker } from 'easy-web-worker';
285
+
286
+ const backgroundWorker = createEasyWebWorker<string, string>(
287
+ ({ onMessage }) => {
288
+ onMessage((message) => {
289
+ message.resolve(`Message from Worker: ${message.payload}`);
290
+ });
291
+ }
292
+ );
293
+
294
+ const result = await backgroundWorker.send('hello!');
179
295
 
180
- message.resolve(`this is a message from the worker: ${payload}`);
296
+ console.log(result);
297
+ ```
298
+
299
+ The first generic controls the `send()` payload. The second controls the value returned when the Worker calls `message.resolve()`.
300
+
301
+ ```ts
302
+ const worker = createEasyWebWorker<Payload, Result>(workerBody);
303
+ ```
304
+
305
+ #### ๐ŸŽฏ Named Worker Methods
306
+
307
+ A Worker can expose multiple message handlers instead of routing everything through one callback.
308
+
309
+ ```ts
310
+ const worker = createEasyWebWorker<string, string>(({ onMessage }) => {
311
+ onMessage((message) => {
312
+ message.resolve(`default: ${message.payload}`);
181
313
  });
182
314
 
183
- // you could also define and send specific methods which allow you to create a better structured API
184
- onMessage<number, number>('doSomething', (message) => {
185
- const { payload } = message;
315
+ onMessage<number, number>('double', (message) => {
316
+ message.resolve(message.payload * 2);
317
+ });
186
318
 
187
- message.resolve(payload + 2);
319
+ onMessage<{ first: number; second: number }, number>('sum', (message) => {
320
+ const { first, second } = message.payload;
321
+ message.resolve(first + second);
188
322
  });
189
323
  });
190
324
 
191
- // outside your worker
192
- const messageResult = await backgroundWorker.send('hello!');
193
-
194
- // for specific methods us the sendToMethod function
195
- const messageResult2 = await backgroundWorker.sendToMethod('doSomething', 2);
325
+ const defaultResult = await worker.send('hello');
326
+ const doubled = await worker.sendToMethod<number, number>('double', 21);
327
+ const total = await worker.sendToMethod<
328
+ number,
329
+ { first: number; second: number }
330
+ >('sum', { first: 20, second: 22 });
196
331
  ```
197
332
 
198
- ### Important notes:
333
+ This makes it possible to build a small, typed API inside one Worker. ๐Ÿงฉ
199
334
 
200
- EasyWebWorker<IPayload, IResult> has two generic parameters... They will affect the typing of the send() and response() methods.
335
+ #### ๐Ÿง  Worker Scope โ€” Important!
201
336
 
202
- - If IResult is null, the _resolve_ method will not require parameters
203
- - If IPayload is null, the _send_ method will not require parameters
337
+ A runtime Worker body becomes the Worker source. It **cannot close over arbitrary variables from the main thread**.
204
338
 
205
- Take into consideration that the _workerBody_ is a template to create a worker in run time, so you'll not be able to use anything outside of the Worker-Scope.
206
-
207
- ```TS
208
- const message = 'Hello';
339
+ ```ts
340
+ const greeting = 'Hello';
209
341
 
210
- await createEasyWebWorker<null, string>(({ onMessage }) => {
342
+ createEasyWebWorker(({ onMessage }) => {
211
343
  onMessage((message) => {
212
-
213
- message.resolve(message); // THIS WILL PRODUCE AND ERROR!! the variable *message* will not exist in Worker-Scope.
344
+ // โŒ `greeting` does not exist inside the Worker scope.
345
+ message.resolve(greeting);
214
346
  });
215
- }).send('hello!');
347
+ });
216
348
  ```
217
349
 
218
- If you need to pass a primitive parameter to the body of the worker, you can use the **primitiveParameters** configuration. This is an array of values that will be serialized and embedded into the worker's body.
350
+ Everything needed by the Worker must be:
351
+
352
+ - Defined inside the Worker body
353
+ - Included through reusable Worker templates
354
+ - Imported as a script
355
+ - Sent in a message
356
+ - Passed as a primitive parameter
357
+
358
+ #### ๐Ÿ“ฆ Primitive Parameters
359
+
360
+ For small static values that should exist when the runtime Worker is created, use `primitiveParameters`.
219
361
 
220
362
  ```ts
221
- const message = 'Hello';
363
+ const prefix = 'Result:';
222
364
 
223
- await createEasyWebWorker<null, string>(
365
+ const worker = createEasyWebWorker<null, string>(
224
366
  ({ onMessage }, context) => {
225
- const [message] = context.primitiveParameters;
367
+ const [prefix] = context.primitiveParameters;
226
368
 
227
- console.log(message); // "hello!" // ๐Ÿ‘ it works!
369
+ onMessage((message) => {
370
+ message.resolve(`${prefix} complete`);
371
+ });
228
372
  },
229
373
  {
230
- primitiveParameters: [message],
374
+ primitiveParameters: [prefix],
231
375
  }
232
- ).send('hello!');
376
+ );
377
+
378
+ console.log(await worker.send()); // Result: complete
233
379
  ```
234
380
 
235
- Take a look at Workers API if you don't know yet how they work: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API,
236
- If you need t to send data to the worker, please define IPayload while creating a worker. _new EasyWebWorker<IPayload>(_
237
- You are just allowed to send information to Workers by messages, and vice versa
381
+ Use regular Worker messages for dynamic application data. `primitiveParameters` are best for small initialization values.
238
382
 
239
- ## IEasyWebWorkerMessage<IPayload = null, IResult = void>
383
+ #### ๐Ÿ“Š Progress Without Resolving
240
384
 
241
- When you defined an onMessage callback in your **Worker**, this will receive all messages from the **send** method:
385
+ The Worker can report progress as many times as necessary before the message finishes.
242
386
 
243
- ```TS
244
- easyWorker.onMessage((message) => {
245
- // the *message* will be strongly typed with TS
387
+ ```ts
388
+ const worker = createEasyWebWorker<number[], number>(({ onMessage }) => {
389
+ onMessage((message) => {
390
+ let total = 0;
246
391
 
247
- // the message could resolve the *send* promise.
248
- message.resolve();
392
+ message.payload.forEach((value, index, values) => {
393
+ total += value;
394
+ message.reportProgress(((index + 1) / values.length) * 100);
395
+ });
249
396
 
250
- // the message could be rejected from the worker
251
- message.reject(new Error());
397
+ message.resolve(total);
398
+ });
399
+ });
400
+
401
+ const result = await worker.send([10, 20, 30, 40]).onProgress((percentage) => {
402
+ console.log(percentage);
403
+ });
404
+ ```
405
+
406
+ Output:
407
+
408
+ ```text
409
+ 25
410
+ 50
411
+ 75
412
+ 100
413
+ ```
414
+
415
+ #### ๐Ÿ›‘ Two-Way Cancellation
416
+
417
+ Cancellation is more than rejecting a promise on the main thread. The Worker receives the cancellation event too.
418
+
419
+ ```ts
420
+ const worker = createEasyWebWorker<number, number>(({ onMessage }) => {
421
+ onMessage((message) => {
422
+ const interval = setInterval(() => {
423
+ // long-running work...
424
+ }, 100);
252
425
 
253
- // this message could be cancelled from inside the worker
254
- message.cancel();
426
+ message.onCancel((data) => {
427
+ clearInterval(interval);
255
428
 
256
- // the message is also able to listen to cancelation evens
257
- message.onCancel(() => {
258
- // release resources
259
- })
429
+ const reason = data.worker_cancelation?.reason ?? data.canceled?.reason;
260
430
 
261
- // you could also report progress to the principal thread if you configured a onProgress callback
262
- message.reportProgress(25);
431
+ console.log('Canceled:', reason);
432
+ });
433
+ });
263
434
  });
435
+
436
+ const task = worker.send(100);
437
+
438
+ task.cancel('User navigated away');
264
439
  ```
265
440
 
266
- ## onProgress
441
+ Inside a Worker, a message can also cancel itself:
267
442
 
268
- Let say you are performing some heavy process in your worker, but you still wanted to implement some kind of progress bar in the main thread... you could add an onProgress callback.
443
+ ```ts
444
+ onMessage((message) => {
445
+ if (!isValid(message.payload)) {
446
+ message.cancel('Invalid payload');
447
+ return;
448
+ }
269
449
 
270
- ```TS
271
- await worker.send().onProgress((progress: number) => {
272
- // change some progress bar percentage
273
- }).then(doSomething);
450
+ // continue...
451
+ });
274
452
  ```
275
453
 
276
- onProgress Is gonna be executed every time you call _message.reportProgress_ inside the worker... the cool part here is that the _reportProgress_ is not gonna finish the main promise returned by the _send_ method.
454
+ #### ๐Ÿ”„ Message Lifecycle
455
+
456
+ Each `IEasyWebWorkerMessage` provides lifecycle hooks:
457
+
458
+ ```ts
459
+ onMessage((message) => {
460
+ const unsubscribeResolve = message.onResolve(() => {});
461
+ const unsubscribeReject = message.onReject(() => {});
462
+ const unsubscribeCancel = message.onCancel(() => {});
463
+ const unsubscribeProgress = message.onProgress(() => {});
464
+ const unsubscribeFinalize = message.onFinalize(() => {});
465
+
466
+ const status = message.getStatus();
467
+ const pending = message.isPending();
468
+
469
+ // Unsubscribe whenever you no longer need a listener.
470
+ unsubscribeResolve();
471
+ unsubscribeReject();
472
+ unsubscribeCancel();
473
+ unsubscribeProgress();
474
+ unsubscribeFinalize();
475
+
476
+ message.resolve();
477
+ });
478
+ ```
277
479
 
278
- ## Having multiple Worker-Templates
480
+ #### ๐Ÿšš Transferable Objects
279
481
 
280
- As _WorkerBody_ are just templates, you could reuse them on other _Workers_, or use them as plugins for your _Workers_. Let's see:
482
+ Both directions can use native `Transferable[]` values to move ownership instead of cloning large buffers.
281
483
 
282
- ```TS
283
- const reusableWorkerSegment: EasyWebWorkerBody = ({ onMessage, close, importScripts }, context) => {
284
- context.doSomething = () => Promise.resolve('This is a plugin example');
484
+ ```ts
485
+ type Payload = {
486
+ buffer: ArrayBuffer;
285
487
  };
286
488
 
287
- const reusableWorkerSegment = await createEasyWebWorker([
288
- reusableWorkerSegment,
289
- ({ onMessage }, context) => onMessage(async (message) => {
290
- // context will have all stuff we added on other plugins
291
- const result = await context.doSomething();
489
+ const worker = createEasyWebWorker<Payload, ArrayBuffer>(({ onMessage }) => {
490
+ onMessage((message) => {
491
+ const { buffer } = message.payload;
292
492
 
293
- message.resolve(result);
294
- })]).send();
493
+ // Process the buffer...
295
494
 
495
+ message.resolve(buffer, [buffer]);
496
+ });
497
+ });
498
+
499
+ const buffer = new ArrayBuffer(1_000_000);
500
+
501
+ const processedBuffer = await worker.send({ buffer }, [buffer]);
296
502
  ```
297
503
 
298
- In this way, you could avoid having to create more than once the same template for your worker.
504
+ This is especially useful for `ArrayBuffer`, media processing, binary parsing, and other data-heavy workloads.
299
505
 
300
- ## Importing scripts into your _Workers_
506
+ ---
301
507
 
302
- Web Workers has this amazing method called importScripts, are you passed an array of strings in the EasyWorker extra configuration, all those files are gonna be imported into your worker.
508
+ ### 2๏ธโƒฃ Static Workers with `StaticEasyWebWorker`
303
509
 
304
- // test.js
510
+ Sometimes a dedicated Worker file is the better architecture โ€” especially when the Worker has its own modules, build pipeline, or large implementation.
305
511
 
306
- ```TS
307
- self.message = 'Hello coders!';
308
- self.doSomething = () => console.log(self.message);
512
+ #### ๐ŸŽช The Basics
513
+
514
+ **`worker.ts`**
515
+
516
+ ```ts
517
+ import { createStaticEasyWebWorker } from 'easy-web-worker/createStaticEasyWebWorker';
518
+
519
+ const { onMessage } = createStaticEasyWebWorker<number, number>();
520
+
521
+ onMessage((message) => {
522
+ message.resolve(message.payload * 2);
523
+ });
524
+
525
+ onMessage<string, string>('uppercase', (message) => {
526
+ message.resolve(message.payload.toUpperCase());
527
+ });
309
528
  ```
310
529
 
311
- ```TS
312
- await createEasyWebWorker(({ onMessage }, context) => {
313
- onMessage((message) => context.doSomething());
314
- }, {
315
- scripts: ['http://localhost:3000/test.js'],
316
- }).send();
530
+ **Main thread**
531
+
532
+ ```ts
533
+ import { createEasyWebWorker } from 'easy-web-worker';
534
+
535
+ const worker = createEasyWebWorker<number, number>('./worker.js');
317
536
 
537
+ const result = await worker.send(21);
318
538
  ```
319
539
 
320
- This is a very simple example, but you could import a whole library into your worker, as _JQUERY_, _Bluebird_ for example
540
+ You keep the `send()`, cancellation, progress, and message lifecycle API while controlling the Worker file yourself.
321
541
 
322
- ## StaticEasyWebWorker
542
+ #### ๐ŸŽ Vite + TypeScript
323
543
 
324
- If you want to create a _Worker_ with a static .js file and don't want to lose the structure of messages and promises and the onProgress callback from the library... you could use StaticEasyWebWorker<IPayload = null, IResult = void>\_ directly in your Worker.
544
+ ```ts
545
+ import workerUrl from './worker?worker&url';
546
+ import { createEasyWebWorker } from 'easy-web-worker';
325
547
 
326
- let's see how to use it:
548
+ const workerSource =
549
+ import.meta.env.MODE === 'production'
550
+ ? workerUrl
551
+ : new URL('./worker.ts', import.meta.url);
327
552
 
328
- // worker.js
329
- // This is gonna be the content of your worker
330
- // onMessage Callback is gonna receive all _send_ method calls.
553
+ const worker = createEasyWebWorker(workerSource, {
554
+ workerOptions: {
555
+ type: 'module',
556
+ },
557
+ });
558
+ ```
331
559
 
332
- ```TS
333
- // imports only the static web worker
334
- import createStaticEasyWebWorker from 'easy-web-worker/createStaticEasyWebWorker';
560
+ #### ๐Ÿ”Œ Existing Native Worker
335
561
 
336
- // this is gonna create the same message structure the runtime Workers
337
- const { onMessage } = createStaticEasyWebWorker();
562
+ Already have a `Worker` instance? Wrap it directly.
338
563
 
339
- onMessage((message) => {
340
- setTimeout(() => {
341
- message.resolve(200);
342
- }, 5000);
564
+ ```ts
565
+ const nativeWorker = new Worker(new URL('./worker.js', import.meta.url), {
566
+ type: 'module',
343
567
  });
344
568
 
345
- onMessage('action', (message) => {
346
- setTimeout(() => {
347
- message.resolve(200);
348
- }, 5000);
569
+ const worker = createEasyWebWorker(nativeWorker);
570
+ ```
571
+
572
+ Or provide an existing pool:
573
+
574
+ ```ts
575
+ const worker = createEasyWebWorker([
576
+ new Worker('./worker-a.js'),
577
+ new Worker('./worker-b.js'),
578
+ ]);
579
+ ```
580
+
581
+ When an existing `Worker[]` is supplied, those Worker instances become the pool managed by `EasyWebWorker`.
582
+
583
+ ---
584
+
585
+ ### 3๏ธโƒฃ Concurrency & Worker Pool
586
+
587
+ A single `EasyWebWorker` can distribute simultaneous messages across multiple native Workers.
588
+
589
+ #### ๐Ÿšฆ Scale on Demand
590
+
591
+ ```ts
592
+ const worker = createEasyWebWorker<number, number>(
593
+ ({ onMessage }) => {
594
+ const fibonacci = (n: number): number => {
595
+ if (n <= 1) return n;
596
+ return fibonacci(n - 1) + fibonacci(n - 2);
597
+ };
598
+
599
+ onMessage((message) => {
600
+ message.resolve(fibonacci(message.payload));
601
+ });
602
+ },
603
+ {
604
+ maxWorkers: 4,
605
+ }
606
+ );
607
+
608
+ const results = await Promise.all([
609
+ worker.send(40),
610
+ worker.send(41),
611
+ worker.send(42),
612
+ ]);
613
+ ```
614
+
615
+ With `maxWorkers: 4`, the library can create additional Workers as concurrent requests arrive, up to the configured limit.
616
+
617
+ #### ๐Ÿ”ฅ Warm Up the Pool
618
+
619
+ Create the full pool immediately:
620
+
621
+ ```ts
622
+ const worker = createEasyWebWorker(workerBody, {
623
+ maxWorkers: 4,
624
+ warmUpWorkers: true,
349
625
  });
350
626
  ```
351
627
 
352
- By the way, if you're in need of a super simple static worker, just know that the first parameter of createStaticEasyWebWorker is a function which will be used as the default onmessage callback.
628
+ Useful when startup latency matters more than keeping the initial resource footprint small.
629
+
630
+ #### ๐Ÿ’ค Dispose Idle Workers Automatically
353
631
 
354
632
  ```ts
355
- createStaticEasyWebWorker((message) => {
356
- // this is the default onMessage
633
+ const worker = createEasyWebWorker(workerBody, {
634
+ maxWorkers: 4,
635
+ keepAlive: false,
636
+ terminationDelay: 5_000,
357
637
  });
358
638
  ```
359
639
 
360
- and in your main thread:
640
+ When there are no queued messages, idle Workers are terminated after the configured delay.
641
+
642
+ #### โš™๏ธ Concurrency Defaults
643
+
644
+ | Option | Current behavior |
645
+ | -------------------------- | --------------------------------------------------------------------------- |
646
+ | `maxWorkers` | Defaults to `1` |
647
+ | `warmUpWorkers` | Defaults to `true` for a single-worker configuration; otherwise `false` |
648
+ | `keepAlive` | Defaults to the resolved `warmUpWorkers` value unless explicitly configured |
649
+ | `terminationDelay` | Defaults to `1000` ms |
650
+ | Existing `Worker[]` source | Pool size comes from the supplied array and the workers are kept alive |
361
651
 
362
- ```TS
363
- const worker = createEasyWebWorker<null,number>('./worker.js');
652
+ This lets the default experience behave like a persistent single Worker while making larger pools opt-in and demand-driven.
653
+
654
+ ---
655
+
656
+ ### 4๏ธโƒฃ Reusable Worker Templates
657
+
658
+ Runtime Worker bodies are templates, so common Worker functionality can be composed.
659
+
660
+ ```ts
661
+ import { createEasyWebWorker, type EasyWebWorkerBody } from 'easy-web-worker';
662
+
663
+ const commonTools: EasyWebWorkerBody = ({ onMessage }, context) => {
664
+ context.doSomething = () => Promise.resolve('Reusable Worker code');
665
+ };
666
+
667
+ const worker = createEasyWebWorker([
668
+ commonTools,
669
+ ({ onMessage }, context) => {
670
+ onMessage(async (message) => {
671
+ const result = await context.doSomething();
672
+
673
+ message.resolve(result);
674
+ });
675
+ },
676
+ ]);
364
677
 
365
678
  await worker.send();
366
679
  ```
367
680
 
368
- Super easy right?
681
+ This is useful when multiple runtime Workers share utilities, message handlers, or initialization logic.
682
+
683
+ ---
369
684
 
370
- ## Concurrency mode
685
+ ### 5๏ธโƒฃ Import Scripts into Runtime Workers
371
686
 
372
- With EasyWebWorker, you can create operations that require heavy concurrency and delegate them to a web workers queue, or create workers on demand depending on the traffic and specific tasks. Let's take a look:
687
+ External scripts can be included through the Worker configuration.
688
+
689
+ ```ts
690
+ const worker = createEasyWebWorker(
691
+ ({ onMessage }, context) => {
692
+ onMessage((message) => {
693
+ context.doSomething();
694
+ message.resolve();
695
+ });
696
+ },
697
+ {
698
+ scripts: ['https://example.com/worker-library.js'],
699
+ }
700
+ );
701
+ ```
702
+
703
+ For example, if the imported script adds something to the Worker global scope:
704
+
705
+ ```js
706
+ // worker-library.js
707
+ self.message = 'Hello from imported script!';
708
+ self.doSomething = () => console.log(self.message);
709
+ ```
710
+
711
+ For modern module-heavy Workers, a static Worker file with normal ESM imports is often easier to organize.
712
+
713
+ ---
714
+
715
+ ## ๐Ÿ”ฅ Advanced Patterns
716
+
717
+ ### ๐Ÿ—๏ธ Build a Worker API with Named Methods
718
+
719
+ ```ts
720
+ type User = {
721
+ id: string;
722
+ name: string;
723
+ };
373
724
 
374
- ```TS
375
- /**
376
- * Notice that the structure of the worker remains the same;
377
- * the only changes are in the configuration parameters of the worker.
378
- * Take a look below.*/
379
725
  const worker = createEasyWebWorker(({ onMessage }) => {
380
- onMessage((message) => {
381
- const { payload } = message;
726
+ const users = new Map<string, User>();
727
+
728
+ onMessage<User, void>('saveUser', (message) => {
729
+ users.set(message.payload.id, message.payload);
730
+ message.resolve();
731
+ });
732
+
733
+ onMessage<string, User | null>('getUser', (message) => {
734
+ message.resolve(users.get(message.payload) ?? null);
735
+ });
736
+
737
+ onMessage<string, boolean>('deleteUser', (message) => {
738
+ message.resolve(users.delete(message.payload));
739
+ });
740
+ });
741
+
742
+ await worker.sendToMethod<void, User>('saveUser', {
743
+ id: '42',
744
+ name: 'Johnny',
745
+ });
746
+
747
+ const user = await worker.sendToMethod<User | null, string>('getUser', '42');
748
+ ```
749
+
750
+ One Worker, multiple typed operations, one shared Worker-local state.
751
+
752
+ ### ๐Ÿ”Ž Filter Large Collections with Progress
753
+
754
+ The Worker can retrieve, store, and filter a large collection without blocking the main thread.
755
+
756
+ ```ts
757
+ type Item = Record<string, unknown>;
382
758
 
383
- // heavy computation like fibonacci
759
+ const worker = createEasyWebWorker<string, Item[]>(({ onMessage }) => {
760
+ const collection = fetch('https://api.example.com/items').then(
761
+ (response) => response.json() as Promise<Item[]>
762
+ );
763
+
764
+ const containsValue = (item: unknown, filter: string): boolean => {
765
+ if (item === null || item === undefined) return false;
766
+
767
+ if (typeof item !== 'object') {
768
+ return String(item).toLowerCase().includes(filter);
769
+ }
770
+
771
+ return Object.values(item).some((value) => containsValue(value, filter));
772
+ };
773
+
774
+ onMessage(async (message) => {
775
+ const items = await collection;
776
+ const filter = message.payload.trim().toLowerCase();
777
+
778
+ const result = items.filter((item, index) => {
779
+ message.reportProgress(((index + 1) / items.length) * 100);
780
+
781
+ return containsValue(item, filter);
782
+ });
384
783
 
385
784
  message.resolve(result);
386
785
  });
387
- }, {
388
- // We will now scale up to four workers if necessary.
389
- maxWorkers: 4
390
786
  });
391
787
  ```
392
788
 
393
- By default, creating an **EasyWebWorker** also creates a single native JavaScript worker. Without any added configuration, this Worker instance will remain active unless it is programmatically disposed. However, by modifying the **maxWorkers** parameter, you can control the number of native workers used, allowing the **EasyWebWorker** to execute multiple messages across multiple threads.
789
+ Usage:
790
+
791
+ ```ts
792
+ const filtered = await worker
793
+ .send('johnny')
794
+ .onProgress((percentage) => console.log(percentage));
795
+
796
+ console.log(filtered);
797
+ ```
394
798
 
395
- You can also control whether these additional workers should be created on demand when messages are sent to the **EasyWebWorker**. This can be done along with setting the **terminationDelay**, which indicates how long to wait before disposing of a **Worker** to avoid unnecessary resource consumption. Alternatively, you can choose to **warmUp** and keep the Workers alive from the moment the **EasyWebWorker** is created.
799
+ ### ๐Ÿฅ‡ Latest Request Wins with `override()`
396
800
 
397
- From the main thread, the consumption of the worker remains the same. Depending on the configuration, the workers will be created either statically or on-demand as needed. The EasyWebWorker will be responsible for selecting them from a pool. Additionally, the EasyWebWorker will manage the distribution of messages among the available workers.
801
+ Useful for search, preview generation, parsing, or any workflow where an older queued result becomes irrelevant.
398
802
 
399
803
  ```ts
400
- // Since maxWorkers is configured to 4, a total of 3 native workers, will be created.
401
- const results = await Promise.all([
402
- worker.send(payload),
403
- worker.send(payload1),
404
- worker.send(payload2),
405
- ]);
804
+ const result = await worker.override(
805
+ latestPayload,
806
+ 'Superseded by a newer request'
807
+ );
406
808
  ```
407
809
 
408
- In the previous example, since the **warmUp** parameter wasnโ€™t included, only 3 Native workers will be created to handle the 3 messages, even though the maximum available is 4. Additionally, since the **keepAlive** parameter was not included, if a total of 1 second passes without new messages, the Native workers will be disposed of to save resources.
810
+ `override()` cancels the current queued work and sends the new message after cancellation completes.
409
811
 
410
- ## Want to see more?
812
+ For immediate cancellation/reboot behavior:
411
813
 
412
- Here is an example of how you could easily create data filter into a Worker, to avoid performing loops process into the main thread that could end affecting user experience.
814
+ ```ts
815
+ await worker.override(latestPayload, 'Superseded', { force: true });
816
+ ```
413
817
 
414
- ```TS
415
- interface FilterSource {
416
- filter: string,
417
- collection: any[],
418
- reportProgress: boolean,
419
- }
818
+ > `force: true` reboots the Worker. A Worker created directly from an existing native `Worker` instance cannot be rebooted by the library.
420
819
 
421
- const worker = createEasyWebWorker<FilterSource, any[]>(({ onMessage }) => {
422
- const containsValue = (item: any, filter: string): boolean => {
423
- const itemKeys = Object.keys(item);
820
+ ### ๐Ÿฅˆ Keep the Current Task, Replace the Rest
424
821
 
425
- return itemKeys.some((key) => {
426
- const prop = item[key] || null;
822
+ `overrideAfterCurrent()` allows the currently executing message to finish, cancels the other queued messages, then sends the replacement.
427
823
 
428
- if (typeof prop !== 'string' && Object.keys(prop).length) return containsValue(prop, filter);
429
- if (prop.toString().replace(/(\r\n|\n|\r)/gm, '').trim().toLowerCase()
430
- .indexOf(filter) !== -1) return true;
824
+ ```ts
825
+ const result = await worker.overrideAfterCurrent(
826
+ latestPayload,
827
+ 'Queue replaced'
828
+ );
829
+ ```
431
830
 
432
- return false;
433
- });
434
- };
831
+ This is useful when interrupting the current operation would be expensive or unsafe, but stale queued work should still be discarded.
435
832
 
436
- onMessage((message: IEasyWebWorkerMessage<FilterSource, any[]>) => {
437
- const { payload } = message;
438
- const { collection, filter = '', reportProgress: countProgress } = payload;
439
- const { length: collectionLength } = collection;
440
- const result = filter === '' ? collection : [];
441
- const progressPerItem = collectionLength ? 100 / collectionLength : 0;
833
+ ### ๐Ÿงน Explicit Cleanup
442
834
 
443
- let currentProgress = 0;
835
+ Dispose the worker when the owning feature or application no longer needs it.
444
836
 
445
- if (filter) {
446
- for (let index = 0; index < collectionLength; index += 1) {
447
- if (countProgress) {
448
- currentProgress += progressPerItem;
449
- message.reportProgress(currentProgress);
450
- }
837
+ ```ts
838
+ await worker.dispose();
839
+ ```
451
840
 
452
- const item = collection[index];
841
+ `dispose()` cancels outstanding work, revokes the generated Worker URL when applicable, terminates Worker instances, and clears the pool.
453
842
 
454
- if (containsValue(item, filter)) result.push(item);
455
- }
456
- }
843
+ ---
457
844
 
458
- message.resolve(result);
459
- });
845
+ ## ๐Ÿงฐ API Reference
846
+
847
+ ### `createEasyWebWorker(source, config?)`
848
+
849
+ Convenience factory that creates an `EasyWebWorker`.
850
+
851
+ Supported sources:
852
+
853
+ ```ts
854
+ EasyWebWorkerBody
855
+ EasyWebWorkerBody[]
856
+ string
857
+ URL
858
+ Worker
859
+ Worker[]
860
+ ```
861
+
862
+ Example:
863
+
864
+ ```ts
865
+ const worker = createEasyWebWorker<Payload, Result>(workerBody, config);
866
+ ```
867
+
868
+ ### `EasyWebWorker<TPayload, TResult>`
869
+
870
+ You can also instantiate the class directly:
871
+
872
+ ```ts
873
+ import { EasyWebWorker } from 'easy-web-worker';
874
+
875
+ const worker = new EasyWebWorker<Payload, Result>(workerBody, config);
876
+ ```
877
+
878
+ ### โš™๏ธ Worker Configuration
879
+
880
+ | Option | Purpose |
881
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
882
+ | `scripts` | Scripts to include in a runtime Worker |
883
+ | `workerOptions` | Native [`WorkerOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) passed when a Worker is created |
884
+ | `onWorkerError` | Handles native Worker errors |
885
+ | `maxWorkers` | Maximum number of native Workers in the pool |
886
+ | `keepAlive` | Keep created Workers alive after work completes |
887
+ | `terminationDelay` | Delay before idle Workers are terminated when `keepAlive` is `false` |
888
+ | `warmUpWorkers` | Create the configured Worker pool during initialization |
889
+ | `primitiveParameters` | Static primitive initialization values exposed through `context.primitiveParameters` |
890
+
891
+ ### ๐Ÿ“ค `send(payload?, transfer?)`
892
+
893
+ Send a message to the default Worker handler.
894
+
895
+ ```ts
896
+ const result = await worker.send(payload);
897
+ ```
898
+
899
+ With transferables:
900
+
901
+ ```ts
902
+ const result = await worker.send(payload, [buffer]);
903
+ ```
904
+
905
+ Returns a `CancelablePromise<TResult>`.
906
+
907
+ ### ๐ŸŽฏ `sendToMethod(method, payload?, transfer?)`
908
+
909
+ Send a message to a named `onMessage(method, callback)` handler.
910
+
911
+ ```ts
912
+ const result = await worker.sendToMethod<Result, Payload>('calculate', payload);
913
+ ```
914
+
915
+ ### ๐Ÿ›‘ `cancelAll(reason?, config?)`
916
+
917
+ Cancel all currently tracked messages.
918
+
919
+ ```ts
920
+ await worker.cancelAll('Canceled by user');
921
+ ```
922
+
923
+ Force an immediate Worker reboot:
924
+
925
+ ```ts
926
+ await worker.cancelAll('Reset', {
927
+ force: true,
460
928
  });
461
929
  ```
462
930
 
463
- And how to use this?
931
+ ### ๐Ÿฅ‡ `override(payload?, reason?, config?)`
932
+
933
+ Cancel current queued work and send a replacement message.
464
934
 
465
- ```TS
466
- worker.send({
467
- collection: [{ name: 'julio perez' }, { name: 'carol starling' }, { name: 'goku' }, { name: { firstname: 'johnny' } }],
468
- filter: 'johnny',
469
- reportProgress: true,
470
- }).onProgress((progressPercentage) => console.log(progressPercentage))
471
- .then((filtered: any[]) => console.log(filtered));
935
+ ```ts
936
+ const result = await worker.override(payload, 'Superseded');
472
937
  ```
473
938
 
474
- the output should be:
475
- => 25
476
- => 50
477
- => 75
478
- => 100
479
- => [{ name: { firstname: 'johnny' } }]
939
+ ### ๐Ÿฅˆ `overrideAfterCurrent(payload?, reason?, config?)`
480
940
 
481
- Of course this is a very tiny array, but is just to give you and idea, actually you also could make fetch requests into workers... give it a try.
941
+ Allow the current message to complete, cancel the remaining queue, then send a replacement.
482
942
 
483
- # Methods
943
+ ```ts
944
+ const result = await worker.overrideAfterCurrent(payload, 'Queue replaced');
945
+ ```
484
946
 
485
- ### `EasyWebWorker.reboot(reason?: unknown): CancelableCancelablePromise<void>[]`
947
+ ### ๐Ÿ”„ `reboot(reason?)`
486
948
 
487
- This method will reboot the worker and cancel all the messages in the queue.
949
+ Terminate the current Worker pool, cancel tracked messages, and initialize the Worker again.
488
950
 
489
- - `reason` - (optional) reason why the worker will be restarted.
951
+ ```ts
952
+ worker.reboot('Worker configuration reset');
953
+ ```
490
954
 
491
- Returns an array of promises that are resolved with the rejection reason provided when the messages are canceled.
955
+ A Worker created from an existing native `Worker` instance cannot be rebooted by the library.
492
956
 
493
- Example usage:
957
+ ### ๐Ÿงน `dispose()`
494
958
 
495
- ```typescript
496
- const worker = createEasyWebWorker<string, string>(({ onMessage }) => {
497
- onMessage((message) => {
498
- message.resolve(`Received message: ${message.payload}`);
499
- });
959
+ Cancel outstanding messages and release the Worker resources.
960
+
961
+ ```ts
962
+ await worker.dispose();
963
+ ```
964
+
965
+ ---
966
+
967
+ ## ๐Ÿ’ฌ `IEasyWebWorkerMessage<TPayload, TResult>`
968
+
969
+ Every `onMessage()` handler receives an `IEasyWebWorkerMessage`.
970
+
971
+ ### ๐Ÿ“ฅ `payload`
972
+
973
+ The payload sent from the main thread.
974
+
975
+ ```ts
976
+ onMessage((message) => {
977
+ console.log(message.payload);
978
+ });
979
+ ```
980
+
981
+ ### โœ… `resolve(result?, transfer?)`
982
+
983
+ Resolve the main-thread promise.
984
+
985
+ ```ts
986
+ message.resolve(result);
987
+ ```
988
+
989
+ ### โŒ `reject(reason?, transfer?)`
990
+
991
+ Reject the main-thread promise.
992
+
993
+ ```ts
994
+ message.reject(new Error('Something failed'));
995
+ ```
996
+
997
+ ### ๐Ÿ›‘ `cancel(reason?, transfer?)`
998
+
999
+ Cancel the request from inside the Worker.
1000
+
1001
+ ```ts
1002
+ message.cancel('No longer needed');
1003
+ ```
1004
+
1005
+ ### ๐Ÿ“Š `reportProgress(percentage, payload?, transfer?)`
1006
+
1007
+ Report progress while keeping the request pending.
1008
+
1009
+ ```ts
1010
+ message.reportProgress(50, {
1011
+ processed: 500,
1012
+ total: 1000,
500
1013
  });
1014
+ ```
501
1015
 
502
- const messagePromise = worker.send('Hello!');
1016
+ ### ๐ŸŽฌ Lifecycle Subscriptions
503
1017
 
504
- worker.reboot('Worker was restarted');
1018
+ ```ts
1019
+ message.onResolve(callback);
1020
+ message.onReject(callback);
1021
+ message.onCancel(callback);
1022
+ message.onProgress(callback);
1023
+ message.onFinalize(callback);
1024
+ ```
1025
+
1026
+ Each subscription returns an unsubscribe function.
505
1027
 
506
- // The message promise will be rejected with the reason 'Worker was restarted'
1028
+ ### ๐Ÿ” Status
1029
+
1030
+ ```ts
1031
+ message.getStatus();
1032
+ message.isPending();
507
1033
  ```
508
1034
 
509
- ### override(payload?, reason?, config?): CancelablePromise
1035
+ ---
1036
+
1037
+ ## ๐ŸŽจ Worker Source Options
510
1038
 
511
- Cancel all current messages and send a new one.
1039
+ | Source | Best for | Worker managed by `easy-web-worker`? |
1040
+ | ------------------- | -------------------------------------------- | ------------------------------------ |
1041
+ | Function template | Small/medium Worker logic with minimal setup | โœ… |
1042
+ | Template array | Composable runtime Worker logic | โœ… |
1043
+ | `string` / `URL` | Dedicated static Worker file | โœ… |
1044
+ | Existing `Worker` | Integrating an existing native Worker | โœ… Wrapped |
1045
+ | Existing `Worker[]` | Supplying your own pre-created pool | โœ… Wrapped |
512
1046
 
513
- ### cancelAll(reason?: unknown): CancelablePromise<void>[]
1047
+ All approaches use the same message-oriented API on the main thread.
514
1048
 
515
- Cancels all messages that are currently waiting to be processed by the worker.
1049
+ ---
516
1050
 
517
- - `reason` - (optional) The reason for the cancellation.
1051
+ ## ๐ŸŽ“ Learning Resources
518
1052
 
519
- Returns an array of promises that are resolved with the rejection reason provided when the messages are canceled.
1053
+ - ๐ŸŒ [Live example โ€” text diff](https://johnny-quesada-developer.github.io/easy-web-workers-example/)
1054
+ - ๐ŸŽฅ [Introduction video](https://www.youtube.com/watch?v=CK-Uri9lDOE)
1055
+ - ๐Ÿงฉ [Example repository](https://github.com/johnny-quesada-developer/easy-web-workers-example)
1056
+ - ๐Ÿงช [CodePen example](https://codepen.io/johnnynabetes/full/wvOvygW)
1057
+ - ๐Ÿ“˜ [MDN โ€” Using Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
1058
+ - ๐Ÿ“ฆ [npm โ€” easy-web-worker](https://www.npmjs.com/package/easy-web-worker)
520
1059
 
521
- ### overrideAfterCurrent(payload?, reason?, config?): CancelablePromise
1060
+ ---
522
1061
 
523
- Cancel all the messages but the current execution and add a new message
1062
+ ## ๐ŸŒ Framework Compatibility
524
1063
 
525
- ### send(payload?, reason?, config?): CancelablePromise
1064
+ `easy-web-worker` is built around the browser's native Worker API, so it is not tied to a UI framework.
1065
+
1066
+ | Environment | Usage |
1067
+ | ------------------ | --------------------- |
1068
+ | React | โœ… |
1069
+ | Angular | โœ… |
1070
+ | Vue | โœ… |
1071
+ | Svelte | โœ… |
1072
+ | Vanilla JavaScript | โœ… |
1073
+ | TypeScript | โœ… Strongly typed API |
1074
+
1075
+ The important requirement is a runtime with browser Web Worker support.
1076
+
1077
+ ---
1078
+
1079
+ ## ๐ŸŽ‰ Why Developers Choose This
1080
+
1081
+ ### The Bottom Line
1082
+
1083
+ | What You Get | What You Avoid |
1084
+ | ----------------------------- | -------------------------------------- |
1085
+ | โœ… Real native Worker threads | โŒ Main-thread CPU bottlenecks |
1086
+ | โœ… Promise-based messaging | โŒ Manual message-ID plumbing |
1087
+ | โœ… Cancellation | โŒ Abandoned long-running work |
1088
+ | โœ… Progress events | โŒ Custom progress protocols |
1089
+ | โœ… Named Worker methods | โŒ Giant message-routing switches |
1090
+ | โœ… Transferable support | โŒ Unnecessary large-data cloning |
1091
+ | โœ… Worker pools | โŒ Hand-written concurrency management |
1092
+ | โœ… Runtime + static Workers | โŒ One forced architecture |
1093
+ | โœ… TypeScript generics | โŒ Untyped request/response contracts |
1094
+
1095
+ ---
1096
+
1097
+ ## ๐Ÿค Collaborators
1098
+
1099
+ <div align="center">
1100
+
1101
+ <table>
1102
+ <tr>
1103
+ <td align="center">
1104
+ <a href="https://github.com/johnny-quesada-developer">
1105
+ <img src="https://avatars.githubusercontent.com/u/62082152?v=4&s=150" width="100" alt="Johnny Quesada" />
1106
+ <br />
1107
+ <sub><b>Johnny Quesada</b></sub>
1108
+ </a>
1109
+ </td>
1110
+ <td align="center">
1111
+ <a href="https://github.com/gabrielecirulli">
1112
+ <img src="https://avatars.githubusercontent.com/u/886011?v=4&s=150" width="100" alt="Gabriele Cirulli" />
1113
+ <br />
1114
+ <sub><b>Gabriele Cirulli</b></sub>
1115
+ </a>
1116
+ </td>
1117
+ </tr>
1118
+ </table>
1119
+
1120
+ </div>
1121
+
1122
+ ---
1123
+
1124
+ ## ๐Ÿš€ Get Started Now
1125
+
1126
+ ```bash
1127
+ npm install easy-web-worker
1128
+ ```
1129
+
1130
+ Then:
1131
+
1132
+ ```ts
1133
+ import { createEasyWebWorker } from 'easy-web-worker';
1134
+
1135
+ const worker = createEasyWebWorker<string, string>(({ onMessage }) => {
1136
+ onMessage((message) => {
1137
+ message.resolve(message.payload.toUpperCase());
1138
+ });
1139
+ });
1140
+
1141
+ console.log(await worker.send('hello worker'));
1142
+ // HELLO WORKER
1143
+ ```
526
1144
 
527
- Sends a message to the worker
1145
+ **That's it. You're running work in a real Web Worker.** ๐ŸŽ‰
528
1146
 
529
- - `payload` - (optional) The message payload.
530
- - `options` - (optional) Additional send options.
1147
+ ---
531
1148
 
532
- **_Thanks for reading, hope this help someone_**
1149
+ <div align="center">
533
1150
 
534
- ## Collaborators
1151
+ ### Built with โค๏ธ for developers who want the main thread to stay responsive
535
1152
 
536
- [![Image Johnny Quesada](https://avatars.githubusercontent.com/u/62082152?v=4&s=150)](https://github.com/johnny-quesada-developer)
537
- &nbsp;&nbsp;&nbsp;&nbsp;[![Image Gabriele Cirulli](https://avatars.githubusercontent.com/u/886011?v=4&s=150)](https://github.com/gabrielecirulli)
1153
+ **[โญ Star on GitHub](https://github.com/johnny-quesada-developer/easy-web-worker)** โ€ข **[๐Ÿ“ Report Issues](https://github.com/johnny-quesada-developer/easy-web-worker/issues)** โ€ข **[๐Ÿงฉ Examples](https://github.com/johnny-quesada-developer/easy-web-workers-example)**
538
1154
 
539
- [Johnny Quesada](https://github.com/johnny-quesada-developer) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Gabriele Cirulli](https://github.com/gabrielecirulli)
1155
+ </div>