0
# Worker Communication
1
2
Message passing system for communication between parent process and worker processes/threads. Jest Worker provides structured messaging capabilities including custom message handling and bidirectional communication patterns.
3
4
## Capabilities
5
6
### Parent-to-Worker Messaging
7
8
The main communication from parent to workers happens automatically through method calls, but understanding the underlying message structure helps with debugging and advanced usage.
9
10
```typescript { .api }
11
// Message types sent from parent to workers
12
type ChildMessage =
13
| ChildMessageInitialize
14
| ChildMessageCall
15
| ChildMessageEnd
16
| ChildMessageMemUsage
17
| ChildMessageCallSetup;
18
19
type ChildMessageInitialize = [
20
type: typeof CHILD_MESSAGE_INITIALIZE,
21
isProcessed: boolean,
22
fileName: string,
23
setupArgs: Array<unknown>,
24
workerId: string | undefined
25
];
26
27
type ChildMessageCall = [
28
type: typeof CHILD_MESSAGE_CALL,
29
isProcessed: boolean,
30
methodName: string,
31
args: Array<unknown>
32
];
33
34
type ChildMessageEnd = [
35
type: typeof CHILD_MESSAGE_END,
36
isProcessed: boolean
37
];
38
39
type ChildMessageMemUsage = [type: typeof CHILD_MESSAGE_MEM_USAGE];
40
41
type ChildMessageCallSetup = [type: typeof CHILD_MESSAGE_CALL_SETUP];
42
43
// Message constants
44
const CHILD_MESSAGE_INITIALIZE = 0;
45
const CHILD_MESSAGE_CALL = 1;
46
const CHILD_MESSAGE_END = 2;
47
const CHILD_MESSAGE_MEM_USAGE = 3;
48
const CHILD_MESSAGE_CALL_SETUP = 4;
49
```
50
51
### Worker-to-Parent Messaging
52
53
Workers send responses and status updates back to the parent process through structured message types.
54
55
```typescript { .api }
56
// Message types sent from workers to parent
57
type ParentMessage =
58
| ParentMessageOk
59
| ParentMessageError
60
| ParentMessageCustom
61
| ParentMessageMemUsage;
62
63
type ParentMessageOk = [
64
type: typeof PARENT_MESSAGE_OK,
65
result: unknown
66
];
67
68
type ParentMessageError = [
69
type: PARENT_MESSAGE_ERROR,
70
constructorName: string,
71
message: string,
72
stack: string,
73
extra: unknown
74
];
75
76
type ParentMessageCustom = [
77
type: typeof PARENT_MESSAGE_CUSTOM,
78
result: unknown
79
];
80
81
type ParentMessageMemUsage = [
82
type: typeof PARENT_MESSAGE_MEM_USAGE,
83
usedMemory: number
84
];
85
86
type PARENT_MESSAGE_ERROR =
87
| typeof PARENT_MESSAGE_CLIENT_ERROR
88
| typeof PARENT_MESSAGE_SETUP_ERROR;
89
90
// Message constants
91
const PARENT_MESSAGE_OK = 0;
92
const PARENT_MESSAGE_CLIENT_ERROR = 1;
93
const PARENT_MESSAGE_SETUP_ERROR = 2;
94
const PARENT_MESSAGE_CUSTOM = 3;
95
const PARENT_MESSAGE_MEM_USAGE = 4;
96
```
97
98
### Custom Message Handling
99
100
Workers can send custom messages to the parent process using the `messageParent` function, and the parent can listen for these messages.
101
102
```typescript { .api }
103
/**
104
* Send custom messages from worker to parent process
105
* @param message - Any serializable data to send
106
* @param parentProcess - Process to send to (defaults to global process)
107
*/
108
function messageParent(
109
message: unknown,
110
parentProcess?: NodeJS.Process
111
): void;
112
```
113
114
**Usage in Worker Module:**
115
116
```javascript
117
// worker.js
118
const { messageParent } = require("jest-worker");
119
120
exports.longRunningTask = async function(data) {
121
// Send progress updates
122
messageParent({ type: "progress", percent: 0 });
123
124
for (let i = 0; i < 100; i++) {
125
// Do work...
126
await processChunk(data, i);
127
128
// Send progress update
129
messageParent({
130
type: "progress",
131
percent: ((i + 1) / 100) * 100,
132
message: `Processed chunk ${i + 1}`
133
});
134
}
135
136
return { completed: true };
137
};
138
139
exports.logStatus = function(message) {
140
messageParent({
141
type: "log",
142
level: "info",
143
message,
144
timestamp: new Date().toISOString()
145
});
146
return "logged";
147
};
148
```
149
150
### Promise with Custom Messages
151
152
Method calls on workers return enhanced promises that support custom message listeners.
153
154
```typescript { .api }
155
/**
156
* Enhanced promise that supports custom message handling
157
*/
158
interface PromiseWithCustomMessage<T> extends Promise<T> {
159
/**
160
* Register listener for custom messages from worker
161
* @param listener - Function to handle custom messages
162
* @returns Function to unregister the listener
163
*/
164
UNSTABLE_onCustomMessage?: (listener: OnCustomMessage) => () => void;
165
}
166
167
/**
168
* Handler for custom messages from workers
169
* @param message - Message data sent from worker
170
*/
171
type OnCustomMessage = (message: Array<unknown> | unknown) => void;
172
```
173
174
**Usage in Parent Process:**
175
176
```typescript
177
import { Worker } from "jest-worker";
178
179
const worker = new Worker(require.resolve("./worker"));
180
181
// Listen for custom messages during task execution
182
const promise = worker.longRunningTask(largeDataSet);
183
184
if (promise.UNSTABLE_onCustomMessage) {
185
const unsubscribe = promise.UNSTABLE_onCustomMessage((message) => {
186
if (message.type === "progress") {
187
console.log(`Progress: ${message.percent}% - ${message.message}`);
188
} else if (message.type === "log") {
189
console.log(`[${message.level}] ${message.message}`);
190
}
191
});
192
193
// Clean up listener when done
194
promise.finally(() => unsubscribe());
195
}
196
197
const result = await promise;
198
console.log("Task completed:", result);
199
```
200
201
### Communication Event Handlers
202
203
Lower-level event handlers for worker communication lifecycle.
204
205
```typescript { .api }
206
/**
207
* Called when a worker task starts processing
208
* @param worker - The worker instance handling the task
209
*/
210
type OnStart = (worker: WorkerInterface) => void;
211
212
/**
213
* Called when a worker task completes or fails
214
* @param err - Error if task failed, null if successful
215
* @param result - Task result if successful
216
*/
217
type OnEnd = (err: Error | null, result: unknown) => void;
218
219
/**
220
* Worker callback function for sending messages
221
* @param workerId - ID of target worker
222
* @param request - Message to send
223
* @param onStart - Handler for task start
224
* @param onEnd - Handler for task completion
225
* @param onCustomMessage - Handler for custom messages
226
*/
227
type WorkerCallback = (
228
workerId: number,
229
request: ChildMessage,
230
onStart: OnStart,
231
onEnd: OnEnd,
232
onCustomMessage: OnCustomMessage
233
) => void;
234
```
235
236
## Advanced Communication Patterns
237
238
### Progress Reporting
239
240
Implement progress reporting for long-running tasks:
241
242
```javascript
243
// worker.js - Progress reporting worker
244
const { messageParent } = require("jest-worker");
245
246
exports.processLargeDataset = async function(dataset) {
247
const total = dataset.length;
248
const results = [];
249
250
for (let i = 0; i < total; i++) {
251
const item = dataset[i];
252
253
// Process item
254
const result = await processItem(item);
255
results.push(result);
256
257
// Report progress every 10 items or on last item
258
if (i % 10 === 0 || i === total - 1) {
259
messageParent({
260
type: "progress",
261
completed: i + 1,
262
total,
263
percent: Math.round(((i + 1) / total) * 100)
264
});
265
}
266
}
267
268
return { results, total: results.length };
269
};
270
```
271
272
```typescript
273
// parent.js - Progress listener
274
const worker = new Worker("./progress-worker.js");
275
276
const promise = worker.processLargeDataset(bigDataset);
277
278
promise.UNSTABLE_onCustomMessage?.((message) => {
279
if (message.type === "progress") {
280
console.log(`Processing: ${message.completed}/${message.total} (${message.percent}%)`);
281
}
282
});
283
284
const results = await promise;
285
```
286
287
### Logging and Debugging
288
289
Send structured log messages from workers:
290
291
```javascript
292
// worker.js - Logging worker
293
const { messageParent } = require("jest-worker");
294
295
function log(level, message, data = {}) {
296
messageParent({
297
type: "log",
298
level,
299
message,
300
data,
301
timestamp: new Date().toISOString(),
302
workerId: process.env.JEST_WORKER_ID
303
});
304
}
305
306
exports.complexTask = async function(input) {
307
log("info", "Starting complex task", { inputSize: input.length });
308
309
try {
310
const preprocessed = await preprocess(input);
311
log("debug", "Preprocessing completed", { outputSize: preprocessed.length });
312
313
const result = await mainProcess(preprocessed);
314
log("info", "Task completed successfully");
315
316
return result;
317
} catch (error) {
318
log("error", "Task failed", { error: error.message, stack: error.stack });
319
throw error;
320
}
321
};
322
```
323
324
### State Synchronization
325
326
Synchronize state between parent and workers:
327
328
```javascript
329
// worker.js - State synchronization
330
const { messageParent } = require("jest-worker");
331
332
let workerState = { processed: 0, cache: new Map() };
333
334
exports.processWithState = function(data) {
335
// Update local state
336
workerState.processed++;
337
workerState.cache.set(data.id, data.result);
338
339
// Sync state to parent
340
messageParent({
341
type: "state-update",
342
workerId: process.env.JEST_WORKER_ID,
343
state: {
344
processed: workerState.processed,
345
cacheSize: workerState.cache.size
346
}
347
});
348
349
return { success: true, processed: workerState.processed };
350
};
351
352
exports.getState = function() {
353
return workerState;
354
};
355
```
356
357
## Error Handling in Communication
358
359
### Worker Error Messages
360
361
Workers automatically send error messages for unhandled exceptions:
362
363
```typescript
364
// Error message structure
365
type ParentMessageError = [
366
type: PARENT_MESSAGE_ERROR,
367
constructorName: string, // Error constructor name (e.g., "TypeError")
368
message: string, // Error message
369
stack: string, // Stack trace
370
extra: unknown // Additional error data
371
];
372
```
373
374
### Custom Error Handling
375
376
Handle communication errors in custom messages:
377
378
```javascript
379
// worker.js - Error handling
380
const { messageParent } = require("jest-worker");
381
382
exports.taskWithCustomErrors = function(data) {
383
try {
384
const result = processData(data);
385
386
// Send success with custom metadata
387
messageParent({
388
type: "task-complete",
389
success: true,
390
metadata: { processingTime: Date.now() - startTime }
391
});
392
393
return result;
394
} catch (error) {
395
// Send custom error message
396
messageParent({
397
type: "task-error",
398
success: false,
399
error: {
400
name: error.constructor.name,
401
message: error.message,
402
code: error.code || "UNKNOWN_ERROR",
403
recoverable: isRecoverableError(error)
404
}
405
});
406
407
throw error; // Still throw for normal error handling
408
}
409
};
410
```
411
412
## Communication Best Practices
413
414
### Message Serialization
415
416
Jest Worker handles serialization automatically, but be aware of limitations:
417
418
```javascript
419
// Supported data types
420
messageParent({
421
string: "text",
422
number: 42,
423
boolean: true,
424
array: [1, 2, 3],
425
object: { key: "value" },
426
null: null,
427
undefined: undefined // Becomes null in serialization
428
});
429
430
// Problematic data types (handled automatically with fallbacks)
431
messageParent({
432
function: () => {}, // Will be serialized using structured cloning
433
symbol: Symbol("test"), // Will be handled with fallback serialization
434
circular: circularObject // Will be handled with structured cloning
435
});
436
```
437
438
### Performance Considerations
439
440
- Custom messages add overhead - use sparingly for frequent operations
441
- Batch multiple updates into single messages when possible
442
- Consider message size for large data transfers
443
- Use worker binding to reduce cross-worker communication