Skip to main content

How do you pass data between threads?

1. How thread interaction is set up

Every thread (Worker) in Node.js:

  • has its own context (doesn't share variables with others),
  • runs within a single process,
  • can exchange messages with the main thread through message passing,
  • can, when needed, share memory via SharedArrayBuffer.

In other words:

Threads can't see each other's global variables, but they can talk through specially provided channels.

2. The basic way: message passing

This is the most common and safest way to exchange data between threads.

Example:

main.js

javascript
const { Worker } = require('worker_threads'); const worker = new Worker('./worker.js'); // Send data to the thread worker.postMessage({ name: 'Tim', numbers: [1, 2, 3] }); // Get the reply worker.on('message', (result) => { console.log('Reply from the thread:', result); });

worker.js

javascript
const { parentPort } = require('worker_threads'); // Receive a message from the main thread parentPort.on('message', (data) => { const sum = data.numbers.reduce((a, b) => a + b, 0); parentPort.postMessage({ greeting: `Hello, ${data.name}!`, sum }); });

How it works:

  • main.js creates a thread and sends data via postMessage().
  • The thread receives it through parentPort.on('message', ...).
  • After computing, it sends the reply back via parentPort.postMessage().

3. Passing data at startup: workerData

You can pass data once, when the thread is created, using the workerData option.

Example:

main.js

javascript
const { Worker } = require('worker_threads'); const worker = new Worker('./worker.js', { workerData: { n: 5 } }); worker.on('message', result => console.log('Result:', result));

worker.js

javascript
const { workerData, parentPort } = require('worker_threads'); let fact = 1; for (let i = 1; i <= workerData.n; i++) fact *= i; parentPort.postMessage({ factorial: fact });

Here, the data (n: 5) is passed right when the worker is created, with no postMessage() needed.

4. Sharing memory: SharedArrayBuffer

This is an advanced technique that lets threads share a region of memory, avoiding copying data and speeding up exchange.

Use it when:

  • you need to exchange large arrays often,
  • top performance matters,
  • low-level operations are acceptable.

Example:

main.js

javascript
const { Worker } = require('worker_threads'); const shared = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT); const sharedArray = new Int32Array(shared); sharedArray[0] = 42; const worker = new Worker('./worker.js', { workerData: shared }); worker.on('exit', () => { console.log('Changed by the worker:', sharedArray[0]); // 84 });

worker.js

javascript
const { workerData } = require('worker_threads'); const arr = new Int32Array(workerData); arr[0] *= 2; // Modify the shared value

Here:

  • SharedArrayBuffer creates a shared memory region available to both threads.
  • Changes made in one thread are visible in the other immediately.
  • Atomics can be used for safe operations (to avoid data races).

5. Passing large data without copying: Transferable objects

Node.js supports passing some objects by reference (instead of copying), for example ArrayBuffer, MessagePort, SharedArrayBuffer.

An example of passing a large buffer without copying:

javascript
const { Worker } = require('worker_threads'); const buffer = new ArrayBuffer(8); const worker = new Worker('./worker.js'); worker.postMessage(buffer, [buffer]); // passed by reference (buffer is now unavailable in main) worker.on('message', () => console.log('The worker got the buffer!'));

worker.js

javascript
const { parentPort } = require('worker_threads'); parentPort.on('message', (buffer) => { console.log('Buffer received in the worker, size:', buffer.byteLength); parentPort.postMessage('OK'); });

After the transfer, the object becomes "detached" (no longer accessible on the sender's side).

6. Summary of ways to pass data

WayDirectionNotes
postMessage() / on('message')Two-wayThe most common way, safe
workerDataInto the worker at creationA one-time transfer at startup
SharedArrayBufferShared memoryVery fast, but needs care
Transferable objectsA one-time transfer by referenceNo copying, the source loses access after the transfer

7. Choosing an approach

ScenarioWhat to use
Ordinary data (objects, JSON, numbers)postMessage()
Passing parameters at startupworkerData
Exchanging large amounts of dataSharedArrayBuffer
Maximum performance, no copyingTransferable objects
Several workers, independent taskspostMessage() + message queues

Summary

CharacteristicValue
Threads are isolated from each otherYes
Are shared variables available?No
How they communicateVia message passing or shared memory
Passing data at startupVia workerData
Shared memorySharedArrayBuffer + Atomics
The safe optionpostMessage()

Conclusion:

In Node.js, threads don't share state directly.

To exchange data, use:

  • messages (postMessage / parentPort), a safe, simple way;
  • shared memory (SharedArrayBuffer), fast, but requires care;
  • transferable objects, for efficiently passing large structures.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.