Featured image of post Node.js -Worker Thread Messages

Node.js -Worker Thread Messages

Message Passing: The Basics

Since Worker Threads don’t share the same execution context, they need to communicate using messages.

This is done via the postMessage() and message event listener.

Each worker has a message port that allows it to send and receive data from the main thread.

Think of it like a walkie-talkie—except instead of shouting, we send structured JSON objects.


Sending and Receiving Messages

Let’s start with a simple example of message passing between a main thread and a worker.

Main Thread (index.js)

1
2
3
4
5
6
7
8
9
const { Worker } = require('worker_threads');

const worker = new Worker('./worker.js');

worker.on('message', (message) => {
    console.log(`Main thread received: ${message}`);
});

worker.postMessage('Hello, Worker!');

Worker Thread (worker.js)

1
2
3
4
5
6
const { parentPort } = require('worker_threads');

parentPort.on('message', (message) => {
    console.log(`Worker received: ${message}`);
    parentPort.postMessage('Hello, Main Thread!');
});

Output

1
2
3
Worker received: Hello, Worker!

Main thread received: Hello, Main Thread!

See?

They’re getting along just fine.


Sending Complex Data Structures

Worker Threads support structured cloning, meaning you can send objects, arrays, and even TypedArrays!

Main Thread Example

1
worker.postMessage({ task: 'processData', payload: [1, 2, 3, 4, 5] });

Worker Thread Example

1
2
3
4
parentPort.on('message', (message) => {
    console.log(`Worker processing:`, message.payload);
    parentPort.postMessage({ result: message.payload.map(num => num * 2) });
});

This allows for more complex and meaningful interactions between threads.


Sharing Memory Between Threads

Sometimes, sending data via messages can be slow for large datasets.

Instead, you can use SharedArrayBuffer to allow direct memory access across threads.

Main Thread Example

1
2
3
const sharedBuffer = new SharedArrayBuffer(1024);
const intArray = new Int32Array(sharedBuffer);
worker.postMessage({ buffer: sharedBuffer });

Worker Thread Example

1
2
3
4
5
parentPort.on('message', ({ buffer }) => {
    const intArray = new Int32Array(buffer);
    intArray[0] = 42;
    parentPort.postMessage('Memory updated!');
});

With SharedArrayBuffer, threads can modify the same memory region without the overhead of message passing.


Handling Multiple Workers

What if you have multiple workers?

You can assign different tasks to each worker and handle their responses separately.

Main Thread Example

1
2
3
4
5
6
7
8
const worker1 = new Worker('./worker.js');
const worker2 = new Worker('./worker.js');

worker1.postMessage('Task for Worker 1');
worker2.postMessage('Task for Worker 2');

worker1.on('message', msg => console.log(`Worker 1: ${msg}`));
worker2.on('message', msg => console.log(`Worker 2: ${msg}`));

This setup lets you efficiently distribute workloads across multiple threads.


When to Use Message Passing vs.

Shared Memory

Use CaseBest Approach
Small data or JSON objectsMessage passing (postMessage)
Large datasetsSharedArrayBuffer
Multiple independent tasksSeparate workers
High-speed memory accessShared memory

Conclusion

  • Worker Threads communicate via message passing using postMessage() and message event listeners.

  • They support structured data like objects, arrays, and buffers.

  • For faster data sharing, use SharedArrayBuffer to avoid serialization overhead.

  • Multiple workers can process tasks in parallel, improving performance for CPU-intensive workloads.


References