Worker threads#
Source Code:lib/worker_threads.js
Thenode:worker_threads module enables the use of threads that executeJavaScript in parallel. To access it:
import worker_threadsfrom'node:worker_threads';'use strict';const worker_threads =require('node:worker_threads');
Workers (threads) are useful for performing CPU-intensive JavaScript operations.They do not help much with I/O-intensive work. The Node.js built-inasynchronous I/O operations are more efficient than Workers can be.
Unlikechild_process orcluster,worker_threads can share memory. They doso by transferringArrayBuffer instances or sharingSharedArrayBufferinstances.
import {Worker, isMainThread, parentPort, workerData,}from'node:worker_threads';if (!isMainThread) {const { parse } =awaitimport('some-js-parsing-library');const script = workerData; parentPort.postMessage(parse(script));}exportdefaultfunctionparseJSAsync(script) {returnnewPromise((resolve, reject) => {const worker =newWorker(newURL(import.meta.url), {workerData: script, }); worker.on('message', resolve); worker.on('error', reject); worker.on('exit',(code) => {if (code !==0)reject(newError(`Worker stopped with exit code${code}`)); }); });};'use strict';const {Worker, isMainThread, parentPort, workerData,} =require('node:worker_threads');if (isMainThread) {module.exports =functionparseJSAsync(script) {returnnewPromise((resolve, reject) => {const worker =newWorker(__filename, {workerData: script, }); worker.on('message', resolve); worker.on('error', reject); worker.on('exit',(code) => {if (code !==0)reject(newError(`Worker stopped with exit code${code}`)); }); }); };}else {const { parse } =require('some-js-parsing-library');const script = workerData; parentPort.postMessage(parse(script));}
The above example spawns a Worker thread for eachparseJSAsync() call. Inpractice, use a pool of Workers for these kinds of tasks. Otherwise, theoverhead of creating Workers would likely exceed their benefit.
When implementing a worker pool, use theAsyncResource API to informdiagnostic tools (e.g. to provide asynchronous stack traces) about thecorrelation between tasks and their outcomes. See"UsingAsyncResource for aWorker thread pool"in theasync_hooks documentation for an example implementation.
Worker threads inherit non-process-specific options by default. Refer toWorker constructor options to know how to customize worker thread options,specificallyargv andexecArgv options.
worker_threads.getEnvironmentData(key)#
History
| Version | Changes |
|---|---|
| v17.5.0, v16.15.0 | No longer experimental. |
| v15.12.0, v14.18.0 | Added in: v15.12.0, v14.18.0 |
Within a worker thread,worker.getEnvironmentData() returns a cloneof data passed to the spawning thread'sworker.setEnvironmentData().Every newWorker receives its own copy of the environment dataautomatically.
import {Worker, isMainThread, setEnvironmentData, getEnvironmentData,}from'node:worker_threads';if (isMainThread) {setEnvironmentData('Hello','World!');const worker =newWorker(newURL(import.meta.url));}else {console.log(getEnvironmentData('Hello'));// Prints 'World!'.}'use strict';const {Worker, isMainThread, setEnvironmentData, getEnvironmentData,} =require('node:worker_threads');if (isMainThread) {setEnvironmentData('Hello','World!');const worker =newWorker(__filename);}else {console.log(getEnvironmentData('Hello'));// Prints 'World!'.}
worker_threads.isInternalThread#
- Type:<boolean>
Istrue if this code is running inside of an internalWorker thread (e.g the loader thread).
node --experimental-loader ./loader.js main.js// loader.jsimport { isInternalThread }from'node:worker_threads';console.log(isInternalThread);// true// loader.js'use strict';const { isInternalThread } =require('node:worker_threads');console.log(isInternalThread);// true
// main.jsimport { isInternalThread }from'node:worker_threads';console.log(isInternalThread);// false// main.js'use strict';const { isInternalThread } =require('node:worker_threads');console.log(isInternalThread);// false
worker_threads.isMainThread#
- Type:<boolean>
Istrue if this code is not running inside of aWorker thread.
import {Worker, isMainThread }from'node:worker_threads';if (isMainThread) {// This re-loads the current file inside a Worker instance.newWorker(newURL(import.meta.url));}else {console.log('Inside Worker!');console.log(isMainThread);// Prints 'false'.}'use strict';const {Worker, isMainThread } =require('node:worker_threads');if (isMainThread) {// This re-loads the current file inside a Worker instance.newWorker(__filename);}else {console.log('Inside Worker!');console.log(isMainThread);// Prints 'false'.}
worker_threads.markAsUntransferable(object)#
object<any> Any arbitrary JavaScript value.
Mark an object as not transferable. Ifobject occurs in the transfer list ofaport.postMessage() call, an error is thrown. This is a no-op ifobject is a primitive value.
In particular, this makes sense for objects that can be cloned, rather thantransferred, and which are used by other objects on the sending side.For example, Node.js marks theArrayBuffers it uses for itsBuffer pool with this.
This operation cannot be undone.
import {MessageChannel, markAsUntransferable }from'node:worker_threads';const pooledBuffer =newArrayBuffer(8);const typedArray1 =newUint8Array(pooledBuffer);const typedArray2 =newFloat64Array(pooledBuffer);markAsUntransferable(pooledBuffer);const { port1 } =newMessageChannel();try {// This will throw an error, because pooledBuffer is not transferable. port1.postMessage(typedArray1, [ typedArray1.buffer ]);}catch (error) {// error.name === 'DataCloneError'}// The following line prints the contents of typedArray1 -- it still owns// its memory and has not been transferred. Without// `markAsUntransferable()`, this would print an empty Uint8Array and the// postMessage call would have succeeded.// typedArray2 is intact as well.console.log(typedArray1);console.log(typedArray2);'use strict';const {MessageChannel, markAsUntransferable } =require('node:worker_threads');const pooledBuffer =newArrayBuffer(8);const typedArray1 =newUint8Array(pooledBuffer);const typedArray2 =newFloat64Array(pooledBuffer);markAsUntransferable(pooledBuffer);const { port1 } =newMessageChannel();try {// This will throw an error, because pooledBuffer is not transferable. port1.postMessage(typedArray1, [ typedArray1.buffer ]);}catch (error) {// error.name === 'DataCloneError'}// The following line prints the contents of typedArray1 -- it still owns// its memory and has not been transferred. Without// `markAsUntransferable()`, this would print an empty Uint8Array and the// postMessage call would have succeeded.// typedArray2 is intact as well.console.log(typedArray1);console.log(typedArray2);
There is no equivalent to this API in browsers.
worker_threads.isMarkedAsUntransferable(object)#
Check if an object is marked as not transferable withmarkAsUntransferable().
import { markAsUntransferable, isMarkedAsUntransferable }from'node:worker_threads';const pooledBuffer =newArrayBuffer(8);markAsUntransferable(pooledBuffer);isMarkedAsUntransferable(pooledBuffer);// Returns true.'use strict';const { markAsUntransferable, isMarkedAsUntransferable } =require('node:worker_threads');const pooledBuffer =newArrayBuffer(8);markAsUntransferable(pooledBuffer);isMarkedAsUntransferable(pooledBuffer);// Returns true.
There is no equivalent to this API in browsers.
worker_threads.markAsUncloneable(object)#
object<any> Any arbitrary JavaScript value.
Mark an object as not cloneable. Ifobject is used asmessage inaport.postMessage() call, an error is thrown. This is a no-op ifobject is aprimitive value.
This has no effect onArrayBuffer, or anyBuffer like objects.
This operation cannot be undone.
import { markAsUncloneable }from'node:worker_threads';const anyObject = {foo:'bar' };markAsUncloneable(anyObject);const { port1 } =newMessageChannel();try {// This will throw an error, because anyObject is not cloneable. port1.postMessage(anyObject);}catch (error) {// error.name === 'DataCloneError'}'use strict';const { markAsUncloneable } =require('node:worker_threads');const anyObject = {foo:'bar' };markAsUncloneable(anyObject);const { port1 } =newMessageChannel();try {// This will throw an error, because anyObject is not cloneable. port1.postMessage(anyObject);}catch (error) {// error.name === 'DataCloneError'}
There is no equivalent to this API in browsers.
worker_threads.moveMessagePortToContext(port, contextifiedSandbox)#
port<MessagePort> The message port to transfer.contextifiedSandbox<Object> Acontextified object as returned by thevm.createContext()method.Returns:<MessagePort>
Transfer aMessagePort to a differentvm Context. The originalportobject is rendered unusable, and the returnedMessagePort instancetakes its place.
The returnedMessagePort is an object in the target context andinherits from its globalObject class. Objects passed to theport.onmessage() listener are also created in the target contextand inherit from its globalObject class.
However, the createdMessagePort no longer inherits from<EventTarget>, and onlyport.onmessage() can be used to receiveevents using it.
worker_threads.parentPort#
- Type:<null> |<MessagePort>
If this thread is aWorker, this is aMessagePortallowing communication with the parent thread. Messages sent usingparentPort.postMessage() are available in the parent threadusingworker.on('message'), and messages sent from the parent threadusingworker.postMessage() are available in this thread usingparentPort.on('message').
import {Worker, isMainThread, parentPort }from'node:worker_threads';if (isMainThread) {const worker =newWorker(newURL(import.meta.url)); worker.once('message',(message) => {console.log(message);// Prints 'Hello, world!'. }); worker.postMessage('Hello, world!');}else {// When a message from the parent thread is received, send it back: parentPort.once('message',(message) => { parentPort.postMessage(message); });}'use strict';const {Worker, isMainThread, parentPort } =require('node:worker_threads');if (isMainThread) {const worker =newWorker(__filename); worker.once('message',(message) => {console.log(message);// Prints 'Hello, world!'. }); worker.postMessage('Hello, world!');}else {// When a message from the parent thread is received, send it back: parentPort.once('message',(message) => { parentPort.postMessage(message); });}
worker_threads.postMessageToThread(threadId, value[, transferList][, timeout])#
threadId<number> The target thread ID. If the thread ID is invalid, aERR_WORKER_MESSAGING_FAILEDerror will be thrown. If the target thread ID is the current thread ID,aERR_WORKER_MESSAGING_SAME_THREADerror will be thrown.value<any> The value to send.transferList<Object[]> If one or moreMessagePort-like objects are passed invalue,atransferListis required for those items orERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LISTis thrown.Seeport.postMessage()for more information.timeout<number> Time to wait for the message to be delivered in milliseconds.By default it'sundefined, which means wait forever. If the operation times out,aERR_WORKER_MESSAGING_TIMEOUTerror is thrown.- Returns:<Promise> A promise which is fulfilled if the message was successfully processed by destination thread.
Sends a value to another worker, identified by its thread ID.
If the target thread has no listener for theworkerMessage event, then the operation will throwaERR_WORKER_MESSAGING_FAILED error.
If the target thread threw an error while processing theworkerMessage event, then the operation will throwaERR_WORKER_MESSAGING_ERRORED error.
This method should be used when the target thread is not the directparent or child of the current thread.If the two threads are parent-children, use therequire('node:worker_threads').parentPort.postMessage()and theworker.postMessage() to let the threads communicate.
The example below shows the use of ofpostMessageToThread: it creates 10 nested threads,the last one will try to communicate with the main thread.
import processfrom'node:process';import { postMessageToThread, threadId, workerData,Worker,}from'node:worker_threads';const channel =newBroadcastChannel('sync');const level = workerData?.level ??0;if (level <10) {const worker =newWorker(newURL(import.meta.url), {workerData: {level: level +1 }, });}if (level ===0) { process.on('workerMessage',(value, source) => {console.log(`${source} ->${threadId}:`, value);postMessageToThread(source, {message:'pong' }); });}elseif (level ===10) { process.on('workerMessage',(value, source) => {console.log(`${source} ->${threadId}:`, value); channel.postMessage('done'); channel.close(); });awaitpostMessageToThread(0, {message:'ping' });}channel.onmessage = channel.close;'use strict';const process =require('node:process');const { postMessageToThread, threadId, workerData,Worker,} =require('node:worker_threads');const channel =newBroadcastChannel('sync');const level = workerData?.level ??0;if (level <10) {const worker =newWorker(__filename, {workerData: {level: level +1 }, });}if (level ===0) { process.on('workerMessage',(value, source) => {console.log(`${source} ->${threadId}:`, value);postMessageToThread(source, {message:'pong' }); });}elseif (level ===10) { process.on('workerMessage',(value, source) => {console.log(`${source} ->${threadId}:`, value); channel.postMessage('done'); channel.close(); });postMessageToThread(0, {message:'ping' });}channel.onmessage = channel.close;
worker_threads.receiveMessageOnPort(port)#
History
| Version | Changes |
|---|---|
| v15.12.0 | The port argument can also refer to a |
| v12.3.0 | Added in: v12.3.0 |
Returns:<Object> |<undefined>
Receive a single message from a givenMessagePort. If no message is available,undefined is returned, otherwise an object with a singlemessage propertythat contains the message payload, corresponding to the oldest message in theMessagePort's queue.
import {MessageChannel, receiveMessageOnPort }from'node:worker_threads';const { port1, port2 } =newMessageChannel();port1.postMessage({hello:'world' });console.log(receiveMessageOnPort(port2));// Prints: { message: { hello: 'world' } }console.log(receiveMessageOnPort(port2));// Prints: undefined'use strict';const {MessageChannel, receiveMessageOnPort } =require('node:worker_threads');const { port1, port2 } =newMessageChannel();port1.postMessage({hello:'world' });console.log(receiveMessageOnPort(port2));// Prints: { message: { hello: 'world' } }console.log(receiveMessageOnPort(port2));// Prints: undefined
When this function is used, no'message' event is emitted and theonmessage listener is not invoked.
worker_threads.resourceLimits#
- Type:<Object>
Provides the set of JS engine resource constraints inside this Worker thread.If theresourceLimits option was passed to theWorker constructor,this matches its values.
If this is used in the main thread, its value is an empty object.
worker_threads.SHARE_ENV#
- Type:<symbol>
A special value that can be passed as theenv option of theWorkerconstructor, to indicate that the current thread and the Worker thread shouldshare read and write access to the same set of environment variables.
import processfrom'node:process';import {Worker,SHARE_ENV }from'node:worker_threads';newWorker('process.env.SET_IN_WORKER = "foo"', {eval:true,env:SHARE_ENV }) .on('exit',() => {console.log(process.env.SET_IN_WORKER);// Prints 'foo'. });'use strict';const {Worker,SHARE_ENV } =require('node:worker_threads');newWorker('process.env.SET_IN_WORKER = "foo"', {eval:true,env:SHARE_ENV }) .on('exit',() => {console.log(process.env.SET_IN_WORKER);// Prints 'foo'. });
worker_threads.setEnvironmentData(key[, value])#
History
| Version | Changes |
|---|---|
| v17.5.0, v16.15.0 | No longer experimental. |
| v15.12.0, v14.18.0 | Added in: v15.12.0, v14.18.0 |
key<any> Any arbitrary, cloneable JavaScript value that can be used as a<Map> key.value<any> Any arbitrary, cloneable JavaScript value that will be clonedand passed automatically to all newWorkerinstances. Ifvalueis passedasundefined, any previously set value for thekeywill be deleted.
Theworker.setEnvironmentData() API sets the content ofworker.getEnvironmentData() in the current thread and all newWorkerinstances spawned from the current context.
worker_threads.threadId#
- Type:<integer>
An integer identifier for the current thread. On the corresponding worker object(if there is any), it is available asworker.threadId.This value is unique for eachWorker instance inside a single process.
worker_threads.threadName#
A string identifier for the current thread or null if the thread is not running.On the corresponding worker object (if there is any), it is available asworker.threadName.
worker_threads.workerData#
An arbitrary JavaScript value that contains a clone of the data passedto this thread'sWorker constructor.
The data is cloned as if usingpostMessage(),according to theHTML structured clone algorithm.
import {Worker, isMainThread, workerData }from'node:worker_threads';if (isMainThread) {const worker =newWorker(newURL(import.meta.url), {workerData:'Hello, world!' });}else {console.log(workerData);// Prints 'Hello, world!'.}'use strict';const {Worker, isMainThread, workerData } =require('node:worker_threads');if (isMainThread) {const worker =newWorker(__filename, {workerData:'Hello, world!' });}else {console.log(workerData);// Prints 'Hello, world!'.}
worker_threads.locks#
An instance of aLockManager that can be used to coordinateaccess to resources that may be shared across multiple threads within the sameprocess. The API mirrors the semantics of thebrowserLockManager
Class:Lock#
TheLock interface provides information about a lock that has been granted vialocks.request()
Class:LockManager#
TheLockManager interface provides methods for requesting and introspectinglocks. To obtain aLockManager instance use
import { locks }from'node:worker_threads';'use strict';const { locks } =require('node:worker_threads');
This implementation matches thebrowserLockManager API.
locks.request(name[, options], callback)#
name<string>options<Object>mode<string> Either'exclusive'or'shared'.Default:'exclusive'.ifAvailable<boolean> Iftrue, the request will only be granted if thelock is not already held. If it cannot be granted,callbackwill beinvoked withnullinstead of aLockinstance.Default:false.steal<boolean> Iftrue, any existing locks with the same name arereleased and the request is granted immediately, pre-empting any queuedrequests.Default:false.signal<AbortSignal> that can be used to abort apending (but not yet granted) lock request.
callback<Function> Invoked once the lock is granted (or immediately withnullififAvailableistrueand the lock is unavailable). The lock isreleased automatically when the function returns, or—if the function returnsa promise—when that promise settles.- Returns:<Promise> Resolves once the lock has been released.
import { locks }from'node:worker_threads';await locks.request('my_resource',async (lock) => {// The lock has been acquired.});// The lock has been released here.'use strict';const { locks } =require('node:worker_threads');locks.request('my_resource',async (lock) => {// The lock has been acquired.}).then(() => {// The lock has been released here.});
locks.query()#
- Returns:<Promise>
Resolves with aLockManagerSnapshot describing the currently held and pendinglocks for the current process.
import { locks }from'node:worker_threads';const snapshot =await locks.query();for (const lockof snapshot.held) {console.log(`held lock: name${lock.name}, mode${lock.mode}`);}for (const pendingof snapshot.pending) {console.log(`pending lock: name${pending.name}, mode${pending.mode}`);}'use strict';const { locks } =require('node:worker_threads');locks.query().then((snapshot) => {for (const lockof snapshot.held) {console.log(`held lock: name${lock.name}, mode${lock.mode}`); }for (const pendingof snapshot.pending) {console.log(`pending lock: name${pending.name}, mode${pending.mode}`); }});
Class:BroadcastChannel extends EventTarget#
History
| Version | Changes |
|---|---|
| v18.0.0 | No longer experimental. |
| v15.4.0 | Added in: v15.4.0 |
Instances ofBroadcastChannel allow asynchronous one-to-many communicationwith all otherBroadcastChannel instances bound to the same channel name.
import { isMainThread,BroadcastChannel,Worker,}from'node:worker_threads';const bc =newBroadcastChannel('hello');if (isMainThread) {let c =0; bc.onmessage =(event) => {console.log(event.data);if (++c ===10) bc.close(); };for (let n =0; n <10; n++)newWorker(newURL(import.meta.url));}else { bc.postMessage('hello from every worker'); bc.close();}'use strict';const { isMainThread,BroadcastChannel,Worker,} =require('node:worker_threads');const bc =newBroadcastChannel('hello');if (isMainThread) {let c =0; bc.onmessage =(event) => {console.log(event.data);if (++c ===10) bc.close(); };for (let n =0; n <10; n++)newWorker(__filename);}else { bc.postMessage('hello from every worker'); bc.close();}
new BroadcastChannel(name)#
name<any> The name of the channel to connect to. Any JavaScript valuethat can be converted to a string using`${name}`is permitted.
broadcastChannel.onmessage#
- Type:<Function> Invoked with a single
MessageEventargumentwhen a message is received.
broadcastChannel.onmessageerror#
- Type:<Function> Invoked with a received message cannot bedeserialized.
broadcastChannel.ref()#
Opposite ofunref(). Callingref() on a previouslyunref()edBroadcastChannel doesnot let the program exit if it's the only active handleleft (the default behavior). If the port isref()ed, callingref() againhas no effect.
broadcastChannel.unref()#
Callingunref() on a BroadcastChannel allows the thread to exit if thisis the only active handle in the event system. If the BroadcastChannel isalreadyunref()ed callingunref() again has no effect.
Class:MessageChannel#
Instances of theworker.MessageChannel class represent an asynchronous,two-way communications channel.TheMessageChannel has no methods of its own.new MessageChannel()yields an object withport1 andport2 properties, which refer to linkedMessagePort instances.
import {MessageChannel }from'node:worker_threads';const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log('received', message));port2.postMessage({foo:'bar' });// Prints: received { foo: 'bar' } from the `port1.on('message')` listener'use strict';const {MessageChannel } =require('node:worker_threads');const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log('received', message));port2.postMessage({foo:'bar' });// Prints: received { foo: 'bar' } from the `port1.on('message')` listener
Class:MessagePort#
History
| Version | Changes |
|---|---|
| v14.7.0 | This class now inherits from |
| v10.5.0 | Added in: v10.5.0 |
- Extends:<EventTarget>
Instances of theworker.MessagePort class represent one end of anasynchronous, two-way communications channel. It can be used to transferstructured data, memory regions and otherMessagePorts between differentWorkers.
This implementation matchesbrowserMessagePorts.
Event:'close'#
The'close' event is emitted once either side of the channel has beendisconnected.
import {MessageChannel }from'node:worker_threads';const { port1, port2 } =newMessageChannel();// Prints:// foobar// closed!port2.on('message',(message) =>console.log(message));port2.on('close',() =>console.log('closed!'));port1.postMessage('foobar');port1.close();'use strict';const {MessageChannel } =require('node:worker_threads');const { port1, port2 } =newMessageChannel();// Prints:// foobar// closed!port2.on('message',(message) =>console.log(message));port2.on('close',() =>console.log('closed!'));port1.postMessage('foobar');port1.close();
Event:'message'#
value<any> The transmitted value
The'message' event is emitted for any incoming message, containing the clonedinput ofport.postMessage().
Listeners on this event receive a clone of thevalue parameter as passedtopostMessage() and no further arguments.
Event:'messageerror'#
error<Error> An Error object
The'messageerror' event is emitted when deserializing a message failed.
Currently, this event is emitted when there is an error occurring whileinstantiating the posted JS object on the receiving end. Such situationsare rare, but can happen, for instance, when certain Node.js API objectsare received in avm.Context (where Node.js APIs are currentlyunavailable).
port.close()#
Disables further sending of messages on either side of the connection.This method can be called when no further communication will happen over thisMessagePort.
The'close' event is emitted on bothMessagePort instances thatare part of the channel.
port.postMessage(value[, transferList])#
History
| Version | Changes |
|---|---|
| v21.0.0 | An error is thrown when an untransferable object is in the transfer list. |
| v15.6.0 | Added |
| v15.0.0 | Added |
| v15.14.0, v14.18.0 | Add 'BlockList' to the list of cloneable types. |
| v15.9.0, v14.18.0 | Add 'Histogram' types to the list of cloneable types. |
| v14.5.0, v12.19.0 | Added |
| v14.5.0, v12.19.0 | Added |
| v10.5.0 | Added in: v10.5.0 |
value<any>transferList<Object[]>
Sends a JavaScript value to the receiving side of this channel.value is transferred in a way which is compatible withtheHTML structured clone algorithm.
In particular, the significant differences toJSON are:
valuemay contain circular references.valuemay contain instances of builtin JS types such asRegExps,BigInts,Maps,Sets, etc.valuemay contain typed arrays, both usingArrayBuffersandSharedArrayBuffers.valuemay containWebAssembly.Moduleinstances.valuemay not contain native (C++-backed) objects other than:
import {MessageChannel }from'node:worker_threads';const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log(message));const circularData = {};circularData.foo = circularData;// Prints: { foo: [Circular] }port2.postMessage(circularData);'use strict';const {MessageChannel } =require('node:worker_threads');const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log(message));const circularData = {};circularData.foo = circularData;// Prints: { foo: [Circular] }port2.postMessage(circularData);
transferList may be a list of<ArrayBuffer>,MessagePort, andFileHandle objects.After transferring, they are not usable on the sending side of the channelanymore (even if they are not contained invalue). Unlike withchild processes, transferring handles such as network sockets is currentlynot supported.
Ifvalue contains<SharedArrayBuffer> instances, those are accessiblefrom either thread. They cannot be listed intransferList.
value may still containArrayBuffer instances that are not intransferList; in that case, the underlying memory is copied rather than moved.
import {MessageChannel }from'node:worker_threads';const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log(message));const uint8Array =newUint8Array([1,2,3,4 ]);// This posts a copy of `uint8Array`:port2.postMessage(uint8Array);// This does not copy data, but renders `uint8Array` unusable:port2.postMessage(uint8Array, [ uint8Array.buffer ]);// The memory for the `sharedUint8Array` is accessible from both the// original and the copy received by `.on('message')`:const sharedUint8Array =newUint8Array(newSharedArrayBuffer(4));port2.postMessage(sharedUint8Array);// This transfers a freshly created message port to the receiver.// This can be used, for example, to create communication channels between// multiple `Worker` threads that are children of the same parent thread.const otherChannel =newMessageChannel();port2.postMessage({port: otherChannel.port1 }, [ otherChannel.port1 ]);'use strict';const {MessageChannel } =require('node:worker_threads');const { port1, port2 } =newMessageChannel();port1.on('message',(message) =>console.log(message));const uint8Array =newUint8Array([1,2,3,4 ]);// This posts a copy of `uint8Array`:port2.postMessage(uint8Array);// This does not copy data, but renders `uint8Array` unusable:port2.postMessage(uint8Array, [ uint8Array.buffer ]);// The memory for the `sharedUint8Array` is accessible from both the// original and the copy received by `.on('message')`:const sharedUint8Array =newUint8Array(newSharedArrayBuffer(4));port2.postMessage(sharedUint8Array);// This transfers a freshly created message port to the receiver.// This can be used, for example, to create communication channels between// multiple `Worker` threads that are children of the same parent thread.const otherChannel =newMessageChannel();port2.postMessage({port: otherChannel.port1 }, [ otherChannel.port1 ]);
The message object is cloned immediately, and can be modified afterposting without having side effects.
For more information on the serialization and deserialization mechanismsbehind this API, see theserialization API of thenode:v8 module.
Considerations when transferring TypedArrays and Buffers#
All<TypedArray> |<Buffer> instances are views over an underlying<ArrayBuffer>. That is, it is theArrayBuffer that actually storesthe raw data while theTypedArray andBuffer objects provide away of viewing and manipulating the data. It is possible and commonfor multiple views to be created over the sameArrayBuffer instance.Great care must be taken when using a transfer list to transfer anArrayBuffer as doing so causes allTypedArray andBufferinstances that share that sameArrayBuffer to become unusable.
const ab =newArrayBuffer(10);const u1 =newUint8Array(ab);const u2 =newUint16Array(ab);console.log(u2.length);// prints 5port.postMessage(u1, [u1.buffer]);console.log(u2.length);// prints 0ForBuffer instances, specifically, whether the underlyingArrayBuffer can be transferred or cloned depends entirely on howinstances were created, which often cannot be reliably determined.
AnArrayBuffer can be marked withmarkAsUntransferable() to indicatethat it should always be cloned and never transferred.
Depending on how aBuffer instance was created, it may or maynot own its underlyingArrayBuffer. AnArrayBuffer must notbe transferred unless it is known that theBuffer instanceowns it. In particular, forBuffers created from the internalBuffer pool (using, for instanceBuffer.from() orBuffer.allocUnsafe()),transferring them is not possible and they are always cloned,which sends a copy of the entireBuffer pool.This behavior may come with unintended higher memoryusage and possible security concerns.
SeeBuffer.allocUnsafe() for more details onBuffer pooling.
TheArrayBuffers forBuffer instances created usingBuffer.alloc() orBuffer.allocUnsafeSlow() can always betransferred but doing so renders all other existing views ofthoseArrayBuffers unusable.
Considerations when cloning objects with prototypes, classes, and accessors#
Because object cloning uses theHTML structured clone algorithm,non-enumerable properties, property accessors, and object prototypes arenot preserved. In particular,<Buffer> objects will be read asplain<Uint8Array>s on the receiving side, and instances of JavaScriptclasses will be cloned as plain JavaScript objects.
const b =Symbol('b');classFoo { #a =1;constructor() {this[b] =2;this.c =3; }getd() {return4; }}const { port1, port2 } =newMessageChannel();port1.onmessage =({ data }) =>console.log(data);port2.postMessage(newFoo());// Prints: { c: 3 }This limitation extends to many built-in objects, such as the globalURLobject:
const { port1, port2 } =newMessageChannel();port1.onmessage =({ data }) =>console.log(data);port2.postMessage(newURL('https://example.org'));// Prints: { }port.hasRef()#
History
| Version | Changes |
|---|---|
| v24.0.0, v22.17.0 | Marking the API stable. |
| v18.1.0, v16.17.0 | Added in: v18.1.0, v16.17.0 |
- Returns:<boolean>
If true, theMessagePort object will keep the Node.js event loop active.
port.ref()#
Opposite ofunref(). Callingref() on a previouslyunref()ed port doesnot let the program exit if it's the only active handle left (the defaultbehavior). If the port isref()ed, callingref() again has no effect.
If listeners are attached or removed using.on('message'), the portisref()ed andunref()ed automatically depending on whetherlisteners for the event exist.
port.start()#
Starts receiving messages on thisMessagePort. When using this portas an event emitter, this is called automatically once'message'listeners are attached.
This method exists for parity with the WebMessagePort API. In Node.js,it is only useful for ignoring messages when no event listener is present.Node.js also diverges in its handling of.onmessage. Setting itautomatically calls.start(), but unsetting it lets messages queue upuntil a new handler is set or the port is discarded.
port.unref()#
Callingunref() on a port allows the thread to exit if this is the onlyactive handle in the event system. If the port is alreadyunref()ed callingunref() again has no effect.
If listeners are attached or removed using.on('message'), the port isref()ed andunref()ed automatically depending on whetherlisteners for the event exist.
Class:Worker#
- Extends:<EventEmitter>
TheWorker class represents an independent JavaScript execution thread.Most Node.js APIs are available inside of it.
Notable differences inside a Worker environment are:
- The
process.stdin,process.stdout, andprocess.stderrstreams may be redirected by the parent thread. - The
require('node:worker_threads').isMainThreadproperty is set tofalse. - The
require('node:worker_threads').parentPortmessage port is available. process.exit()does not stop the whole program, just the single thread,andprocess.abort()is not available.process.chdir()andprocessmethods that set group or user idsare not available.process.envis a copy of the parent thread's environment variables,unless otherwise specified. Changes to one copy are not visible in otherthreads, and are not visible to native add-ons (unlessworker.SHARE_ENVis passed as theenvoption to theWorkerconstructor). On Windows, unlike the main thread, a copy of theenvironment variables operates in a case-sensitive manner.process.titlecannot be modified.- Signals are not delivered through
process.on('...'). - Execution may stop at any point as a result of
worker.terminate()being invoked. - IPC channels from parent processes are not accessible.
- The
trace_eventsmodule is not supported. - Native add-ons can only be loaded from multiple threads if they fulfillcertain conditions.
CreatingWorker instances inside of otherWorkers is possible.
LikeWeb Workers and thenode:cluster module, two-way communicationcan be achieved through inter-thread message passing. Internally, aWorker hasa built-in pair ofMessagePorts that are already associated with eachother when theWorker is created. While theMessagePort object on the parentside is not directly exposed, its functionalities are exposed throughworker.postMessage() and theworker.on('message') eventon theWorker object for the parent thread.
To create custom messaging channels (which is encouraged over using the defaultglobal channel because it facilitates separation of concerns), users can createaMessageChannel object on either thread and pass one of theMessagePorts on thatMessageChannel to the other thread through apre-existing channel, such as the global one.
Seeport.postMessage() for more information on how messages are passed,and what kind of JavaScript values can be successfully transported throughthe thread barrier.
import assertfrom'node:assert';import {Worker,MessageChannel,MessagePort, isMainThread, parentPort,}from'node:worker_threads';if (isMainThread) {const worker =newWorker(newURL(import.meta.url));const subChannel =newMessageChannel(); worker.postMessage({hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message',(value) => {console.log('received:', value); });}else { parentPort.once('message',(value) => {assert(value.hereIsYourPortinstanceofMessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); });}'use strict';const assert =require('node:assert');const {Worker,MessageChannel,MessagePort, isMainThread, parentPort,} =require('node:worker_threads');if (isMainThread) {const worker =newWorker(__filename);const subChannel =newMessageChannel(); worker.postMessage({hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message',(value) => {console.log('received:', value); });}else { parentPort.once('message',(value) => {assert(value.hereIsYourPortinstanceofMessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); });}
new Worker(filename[, options])#
History
| Version | Changes |
|---|---|
| v19.8.0, v18.16.0 | Added support for a |
| v14.9.0 | The |
| v14.9.0 | The |
| v14.6.0, v12.19.0 | The |
| v13.13.0, v12.17.0 | The |
| v13.12.0, v12.17.0 | The |
| v13.4.0, v12.16.0 | The |
| v13.2.0, v12.16.0 | The |
| v10.5.0 | Added in: v10.5.0 |
filename<string> |<URL> The path to the Worker's main script or module. Mustbe either an absolute path or a relative path (i.e. relative to thecurrent working directory) starting with./or../, or a WHATWGURLobject usingfile:ordata:protocol.When using adata:URL, the data is interpreted based on MIME type usingtheECMAScript module loader.Ifoptions.evalistrue, this is a string containing JavaScript coderather than a path.options<Object>argv<any[]> List of arguments which would be stringified and appended toprocess.argvin the worker. This is mostly similar to theworkerDatabut the values are available on the globalprocess.argvas if theywere passed as CLI options to the script.env<Object> If set, specifies the initial value ofprocess.envinsidethe Worker thread. As a special value,worker.SHARE_ENVmay be usedto specify that the parent thread and the child thread should share theirenvironment variables; in that case, changes to one thread'sprocess.envobject affect the other thread as well.Default:process.env.eval<boolean> Iftrueand the first argument is astring, interpretthe first argument to the constructor as a script that is executed once theworker is online.execArgv<string[]> List of node CLI options passed to the worker.V8 options (such as--max-old-space-size) and options that affect theprocess (such as--title) are not supported. If set, this is providedasprocess.execArgvinside the worker. By default, options areinherited from the parent thread.stdin<boolean> If this is set totrue, thenworker.stdinprovides a writable stream whose contents appear asprocess.stdininside the Worker. By default, no data is provided.stdout<boolean> If this is set totrue, thenworker.stdoutisnot automatically piped through toprocess.stdoutin the parent.stderr<boolean> If this is set totrue, thenworker.stderrisnot automatically piped through toprocess.stderrin the parent.workerData<any> Any JavaScript value that is cloned and madeavailable asrequire('node:worker_threads').workerData. The cloningoccurs as described in theHTML structured clone algorithm, and an erroris thrown if the object cannot be cloned (e.g. because it containsfunctions).trackUnmanagedFds<boolean> If this is set totrue, then the Workertracks raw file descriptors managed throughfs.open()andfs.close(), and closes them when the Worker exits, similar to otherresources like network sockets or file descriptors managed throughtheFileHandleAPI. This option is automatically inherited by allnestedWorkers.Default:true.transferList<Object[]> If one or moreMessagePort-like objectsare passed inworkerData, atransferListis required for thoseitems orERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LISTis thrown.Seeport.postMessage()for more information.resourceLimits<Object> An optional set of resource limits for the new JSengine instance. Reaching these limits leads to termination of theWorkerinstance. These limits only affect the JS engine, and no external data,including noArrayBuffers. Even if these limits are set, the process maystill abort if it encounters a global out-of-memory situation.maxOldGenerationSizeMb<number> The maximum size of the main heap inMB. If the command-line argument--max-old-space-sizeis set, itoverrides this setting.maxYoungGenerationSizeMb<number> The maximum size of a heap space forrecently created objects. If the command-line argument--max-semi-space-sizeis set, it overrides this setting.codeRangeSizeMb<number> The size of a pre-allocated memory rangeused for generated code.stackSizeMb<number> The default maximum stack size for the thread.Small values may lead to unusable Worker instances.Default:4.
name<string> An optionalnameto be replaced in the thread nameand to the worker title for debugging/identification purposes,making the final title as[worker ${id}] ${name}.This parameter has a maximum allowed size, depending on the operatingsystem. If the provided name exceeds the limit, it will be truncated- Maximum sizes:
- Windows: 32,767 characters
- macOS: 64 characters
- Linux: 16 characters
- NetBSD: limited to
PTHREAD_MAX_NAMELEN_NP - FreeBSD and OpenBSD: limited to
MAXCOMLENDefault:'WorkerThread'.
- Maximum sizes:
Event:'error'#
err<any>
The'error' event is emitted if the worker thread throws an uncaughtexception. In that case, the worker is terminated.
Event:'exit'#
exitCode<integer>
The'exit' event is emitted once the worker has stopped. If the workerexited by callingprocess.exit(), theexitCode parameter is thepassed exit code. If the worker was terminated, theexitCode parameter is1.
This is the final event emitted by anyWorker instance.
Event:'message'#
value<any> The transmitted value
The'message' event is emitted when the worker thread has invokedrequire('node:worker_threads').parentPort.postMessage().See theport.on('message') event for more details.
All messages sent from the worker thread are emitted before the'exit' event is emitted on theWorker object.
Event:'messageerror'#
error<Error> An Error object
The'messageerror' event is emitted when deserializing a message failed.
Event:'online'#
The'online' event is emitted when the worker thread has started executingJavaScript code.
worker.cpuUsage([prev])#
- Returns:<Promise>
This method returns aPromise that will resolve to an object identical toprocess.threadCpuUsage(),or reject with anERR_WORKER_NOT_RUNNING error if the worker is no longer running.This methods allows the statistics to be observed from outside the actual thread.
worker.getHeapSnapshot([options])#
History
| Version | Changes |
|---|---|
| v19.1.0 | Support options to configure the heap snapshot. |
| v13.9.0, v12.17.0 | Added in: v13.9.0, v12.17.0 |
Returns a readable stream for a V8 snapshot of the current state of the Worker.Seev8.getHeapSnapshot() for more details.
If the Worker thread is no longer running, which may occur before the'exit' event is emitted, the returnedPromise is rejectedimmediately with anERR_WORKER_NOT_RUNNING error.
worker.getHeapStatistics()#
- Returns:<Promise>
This method returns aPromise that will resolve to an object identical tov8.getHeapStatistics(),or reject with anERR_WORKER_NOT_RUNNING error if the worker is no longer running.This methods allows the statistics to be observed from outside the actual thread.
worker.performance#
An object that can be used to query performance information from a workerinstance.
performance.eventLoopUtilization([utilization1[, utilization2]])#
utilization1<Object> The result of a previous call toeventLoopUtilization().utilization2<Object> The result of a previous call toeventLoopUtilization()prior toutilization1.- Returns:<Object>
The same call asperf_hookseventLoopUtilization(), except the valuesof the worker instance are returned.
One difference is that, unlike the main thread, bootstrapping within a workeris done within the event loop. So the event loop utilization isimmediately available once the worker's script begins execution.
Anidle time that does not increase does not indicate that the worker isstuck in bootstrap. The following examples shows how the worker's entirelifetime never accumulates anyidle time, but is still be able to processmessages.
import {Worker, isMainThread, parentPort }from'node:worker_threads';if (isMainThread) {const worker =newWorker(newURL(import.meta.url));setInterval(() => { worker.postMessage('hi');console.log(worker.performance.eventLoopUtilization()); },100).unref();}else { parentPort.on('message',() =>console.log('msg')).unref(); (functionr(n) {if (--n <0)return;const t =Date.now();while (Date.now() - t <300);setImmediate(r, n); })(10);}'use strict';const {Worker, isMainThread, parentPort } =require('node:worker_threads');if (isMainThread) {const worker =newWorker(__filename);setInterval(() => { worker.postMessage('hi');console.log(worker.performance.eventLoopUtilization()); },100).unref();}else { parentPort.on('message',() =>console.log('msg')).unref(); (functionr(n) {if (--n <0)return;const t =Date.now();while (Date.now() - t <300);setImmediate(r, n); })(10);}
The event loop utilization of a worker is available only after the'online'event emitted, and if called before this, or after the'exit'event, then all properties have the value of0.
worker.postMessage(value[, transferList])#
value<any>transferList<Object[]>
Send a message to the worker that is received viarequire('node:worker_threads').parentPort.on('message').Seeport.postMessage() for more details.
worker.ref()#
Opposite ofunref(), callingref() on a previouslyunref()ed worker doesnot let the program exit if it's the only active handle left (the defaultbehavior). If the worker isref()ed, callingref() again hasno effect.
worker.resourceLimits#
- Type:<Object>
Provides the set of JS engine resource constraints for this Worker thread.If theresourceLimits option was passed to theWorker constructor,this matches its values.
If the worker has stopped, the return value is an empty object.
worker.startCpuProfile()#
- Returns:<Promise>
Starting a CPU profile then return a Promise that fulfills with an erroror anCPUProfileHandle object. This API supportsawait using syntax.
const {Worker } =require('node:worker_threads');const worker =newWorker(` const { parentPort } = require('worker_threads'); parentPort.on('message', () => {}); `, {eval:true });worker.on('online',async () => {const handle =await worker.startCpuProfile();const profile =await handle.stop();console.log(profile); worker.terminate();});await using example.
const {Worker } =require('node:worker_threads');const w =newWorker(` const { parentPort } = require('node:worker_threads'); parentPort.on('message', () => {}); `, {eval:true });w.on('online',async () => {// Stop profile automatically when return and profile will be discardedawaitusing handle =await w.startCpuProfile();});worker.startHeapProfile()#
- Returns:<Promise>
Starting a Heap profile then return a Promise that fulfills with an erroror anHeapProfileHandle object. This API supportsawait using syntax.
const {Worker } =require('node:worker_threads');const worker =newWorker(` const { parentPort } = require('worker_threads'); parentPort.on('message', () => {}); `, {eval:true });worker.on('online',async () => {const handle =await worker.startHeapProfile();const profile =await handle.stop();console.log(profile); worker.terminate();});await using example.
const {Worker } =require('node:worker_threads');const w =newWorker(` const { parentPort } = require('node:worker_threads'); parentPort.on('message', () => {}); `, {eval:true });w.on('online',async () => {// Stop profile automatically when return and profile will be discardedawaitusing handle =await w.startHeapProfile();});worker.stderr#
- Type:<stream.Readable>
This is a readable stream which contains data written toprocess.stderrinside the worker thread. Ifstderr: true was not passed to theWorker constructor, then data is piped to the parent thread'sprocess.stderr stream.
worker.stdin#
- Type:<null> |<stream.Writable>
Ifstdin: true was passed to theWorker constructor, this is awritable stream. The data written to this stream will be made available inthe worker thread asprocess.stdin.
worker.stdout#
- Type:<stream.Readable>
This is a readable stream which contains data written toprocess.stdoutinside the worker thread. Ifstdout: true was not passed to theWorker constructor, then data is piped to the parent thread'sprocess.stdout stream.
worker.terminate()#
History
| Version | Changes |
|---|---|
| v12.5.0 | This function now returns a Promise. Passing a callback is deprecated, and was useless up to this version, as the Worker was actually terminated synchronously. Terminating is now a fully asynchronous operation. |
| v10.5.0 | Added in: v10.5.0 |
- Returns:<Promise>
Stop all JavaScript execution in the worker thread as soon as possible.Returns a Promise for the exit code that is fulfilled when the'exit' event is emitted.
worker.threadId#
- Type:<integer>
An integer identifier for the referenced thread. Inside the worker thread,it is available asrequire('node:worker_threads').threadId.This value is unique for eachWorker instance inside a single process.
worker.threadName#
A string identifier for the referenced thread or null if the thread is not running.Inside the worker thread, it is available asrequire('node:worker_threads').threadName.
worker.unref()#
Callingunref() on a worker allows the thread to exit if this is the onlyactive handle in the event system. If the worker is alreadyunref()ed callingunref() again has no effect.
worker[Symbol.asyncDispose]()#
Callsworker.terminate() when the dispose scope is exited.
asyncfunctionexample() {awaitusing worker =newWorker('for (;;) {}', {eval:true });// Worker is automatically terminate when the scope is exited.}Notes#
Synchronous blocking of stdio#
Workers utilize message passing via<MessagePort> to implement interactionswithstdio. This means thatstdio output originating from aWorker canget blocked by synchronous code on the receiving end that is blocking theNode.js event loop.
import {Worker, isMainThread,}from'node:worker_threads';if (isMainThread) {newWorker(newURL(import.meta.url));for (let n =0; n <1e10; n++) {// Looping to simulate work. }}else {// This output will be blocked by the for loop in the main thread.console.log('foo');}'use strict';const {Worker, isMainThread,} =require('node:worker_threads');if (isMainThread) {newWorker(__filename);for (let n =0; n <1e10; n++) {// Looping to simulate work. }}else {// This output will be blocked by the for loop in the main thread.console.log('foo');}
Launching worker threads from preload scripts#
Take care when launching worker threads from preload scripts (scripts loadedand run using the-r command line flag). Unless theexecArgv option isexplicitly set, new Worker threads automatically inherit the command line flagsfrom the running process and will preload the same preload scripts as the mainthread. If the preload script unconditionally launches a worker thread, everythread spawned will spawn another until the application crashes.