or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdtask-queues.mdworker-communication.mdworker-management.md
tile.json

worker-communication.mddocs/

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