worker: add ESM version examples to worker docs

PR-URL: https://github.com/nodejs/node/pull/57645
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
This commit is contained in:
fisker Cheung 2025-04-20 01:26:38 +08:00 committed by GitHub
parent 609df89cd3
commit 0699a99bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 361 additions and 39 deletions

View File

@ -9,7 +9,13 @@
The `node:worker_threads` module enables the use of threads that execute The `node:worker_threads` module enables the use of threads that execute
JavaScript in parallel. To access it: JavaScript in parallel. To access it:
```js ```mjs
import worker from 'node:worker_threads';
```
```cjs
'use strict';
const worker = require('node:worker_threads'); const worker = require('node:worker_threads');
``` ```
@ -21,9 +27,43 @@ Unlike `child_process` or `cluster`, `worker_threads` can share memory. They do
so by transferring `ArrayBuffer` instances or sharing `SharedArrayBuffer` so by transferring `ArrayBuffer` instances or sharing `SharedArrayBuffer`
instances. instances.
```js ```mjs
import {
Worker,
isMainThread,
parentPort,
workerData,
} from 'node:worker_threads';
if (!isMainThread) {
const { parse } = await import('some-js-parsing-library');
const script = workerData;
parentPort.postMessage(parse(script));
}
export default function parseJSAsync(script) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL(import.meta.url), {
workerData: script,
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker stopped with exit code ${code}`));
});
});
};
```
```cjs
'use strict';
const { const {
Worker, isMainThread, parentPort, workerData, Worker,
isMainThread,
parentPort,
workerData,
} = require('node:worker_threads'); } = require('node:worker_threads');
if (isMainThread) { if (isMainThread) {
@ -84,7 +124,25 @@ of data passed to the spawning thread's `worker.setEnvironmentData()`.
Every new `Worker` receives its own copy of the environment data Every new `Worker` receives its own copy of the environment data
automatically. automatically.
```js ```mjs
import {
Worker,
isMainThread,
setEnvironmentData,
getEnvironmentData,
} from 'node:worker_threads';
if (isMainThread) {
setEnvironmentData('Hello', 'World!');
const worker = new Worker(new URL(import.meta.url));
} else {
console.log(getEnvironmentData('Hello')); // Prints 'World!'.
}
```
```cjs
'use strict';
const { const {
Worker, Worker,
isMainThread, isMainThread,
@ -116,12 +174,6 @@ Is `true` if this code is running inside of an internal [`Worker`][] thread (e.g
node --experimental-loader ./loader.js main.js node --experimental-loader ./loader.js main.js
``` ```
```cjs
// loader.js
const { isInternalThread } = require('node:worker_threads');
console.log(isInternalThread); // true
```
```mjs ```mjs
// loader.js // loader.js
import { isInternalThread } from 'node:worker_threads'; import { isInternalThread } from 'node:worker_threads';
@ -129,14 +181,24 @@ console.log(isInternalThread); // true
``` ```
```cjs ```cjs
// main.js // loader.js
'use strict';
const { isInternalThread } = require('node:worker_threads'); const { isInternalThread } = require('node:worker_threads');
console.log(isInternalThread); // true
```
```mjs
// main.js
import { isInternalThread } from 'node:worker_threads';
console.log(isInternalThread); // false console.log(isInternalThread); // false
``` ```
```mjs ```cjs
// main.js // main.js
import { isInternalThread } from 'node:worker_threads'; 'use strict';
const { isInternalThread } = require('node:worker_threads');
console.log(isInternalThread); // false console.log(isInternalThread); // false
``` ```
@ -150,7 +212,21 @@ added: v10.5.0
Is `true` if this code is not running inside of a [`Worker`][] thread. Is `true` if this code is not running inside of a [`Worker`][] thread.
```js ```mjs
import { Worker, isMainThread } from 'node:worker_threads';
if (isMainThread) {
// This re-loads the current file inside a Worker instance.
new Worker(new URL(import.meta.url));
} else {
console.log('Inside Worker!');
console.log(isMainThread); // Prints 'false'.
}
```
```cjs
'use strict';
const { Worker, isMainThread } = require('node:worker_threads'); const { Worker, isMainThread } = require('node:worker_threads');
if (isMainThread) { if (isMainThread) {
@ -183,7 +259,35 @@ For example, Node.js marks the `ArrayBuffer`s it uses for its
This operation cannot be undone. This operation cannot be undone.
```js ```mjs
import { MessageChannel, markAsUntransferable } from 'node:worker_threads';
const pooledBuffer = new ArrayBuffer(8);
const typedArray1 = new Uint8Array(pooledBuffer);
const typedArray2 = new Float64Array(pooledBuffer);
markAsUntransferable(pooledBuffer);
const { port1 } = new MessageChannel();
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);
```
```cjs
'use strict';
const { MessageChannel, markAsUntransferable } = require('node:worker_threads'); const { MessageChannel, markAsUntransferable } = require('node:worker_threads');
const pooledBuffer = new ArrayBuffer(8); const pooledBuffer = new ArrayBuffer(8);
@ -223,7 +327,18 @@ added: v21.0.0
Check if an object is marked as not transferable with Check if an object is marked as not transferable with
[`markAsUntransferable()`][]. [`markAsUntransferable()`][].
```js ```mjs
import { markAsUntransferable, isMarkedAsUntransferable } from 'node:worker_threads';
const pooledBuffer = new ArrayBuffer(8);
markAsUntransferable(pooledBuffer);
isMarkedAsUntransferable(pooledBuffer); // Returns true.
```
```cjs
'use strict';
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads'); const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads');
const pooledBuffer = new ArrayBuffer(8); const pooledBuffer = new ArrayBuffer(8);
@ -252,7 +367,23 @@ This has no effect on `ArrayBuffer`, or any `Buffer` like objects.
This operation cannot be undone. This operation cannot be undone.
```js ```mjs
import { markAsUncloneable } from 'node:worker_threads';
const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
// This will throw an error, because anyObject is not cloneable.
port1.postMessage(anyObject);
} catch (error) {
// error.name === 'DataCloneError'
}
```
```cjs
'use strict';
const { markAsUncloneable } = require('node:worker_threads'); const { markAsUncloneable } = require('node:worker_threads');
const anyObject = { foo: 'bar' }; const anyObject = { foo: 'bar' };
@ -309,7 +440,26 @@ using `worker.on('message')`, and messages sent from the parent thread
using `worker.postMessage()` are available in this thread using using `worker.postMessage()` are available in this thread using
`parentPort.on('message')`. `parentPort.on('message')`.
```js ```mjs
import { Worker, isMainThread, parentPort } from 'node:worker_threads';
if (isMainThread) {
const worker = new Worker(new URL(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);
});
}
```
```cjs
'use strict';
const { Worker, isMainThread, parentPort } = require('node:worker_threads'); const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) { if (isMainThread) {
@ -365,7 +515,6 @@ The example below shows the use of of `postMessageToThread`: it creates 10 neste
the last one will try to communicate with the main thread. the last one will try to communicate with the main thread.
```mjs ```mjs
import { fileURLToPath } from 'node:url';
import process from 'node:process'; import process from 'node:process';
import { import {
postMessageToThread, postMessageToThread,
@ -378,7 +527,7 @@ const channel = new BroadcastChannel('sync');
const level = workerData?.level ?? 0; const level = workerData?.level ?? 0;
if (level < 10) { if (level < 10) {
const worker = new Worker(fileURLToPath(import.meta.url), { const worker = new Worker(new URL(import.meta.url), {
workerData: { level: level + 1 }, workerData: { level: level + 1 },
}); });
} }
@ -402,6 +551,9 @@ channel.onmessage = channel.close;
``` ```
```cjs ```cjs
'use strict';
const process = require('node:process');
const { const {
postMessageToThread, postMessageToThread,
threadId, threadId,
@ -455,7 +607,20 @@ Receive a single message from a given `MessagePort`. If no message is available,
that contains the message payload, corresponding to the oldest message in the that contains the message payload, corresponding to the oldest message in the
`MessagePort`'s queue. `MessagePort`'s queue.
```js ```mjs
import { MessageChannel, receiveMessageOnPort } from 'node:worker_threads';
const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' });
console.log(receiveMessageOnPort(port2));
// Prints: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2));
// Prints: undefined
```
```cjs
'use strict';
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads'); const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' }); port1.postMessage({ hello: 'world' });
@ -501,7 +666,18 @@ A special value that can be passed as the `env` option of the [`Worker`][]
constructor, to indicate that the current thread and the Worker thread should constructor, to indicate that the current thread and the Worker thread should
share read and write access to the same set of environment variables. share read and write access to the same set of environment variables.
```js ```mjs
import process from 'node:process';
import { Worker, SHARE_ENV } from 'node:worker_threads';
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
.on('exit', () => {
console.log(process.env.SET_IN_WORKER); // Prints 'foo'.
});
```
```cjs
'use strict';
const { Worker, SHARE_ENV } = require('node:worker_threads'); const { Worker, SHARE_ENV } = require('node:worker_threads');
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV }) new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
.on('exit', () => { .on('exit', () => {
@ -557,7 +733,19 @@ to this thread's `Worker` constructor.
The data is cloned as if using [`postMessage()`][`port.postMessage()`], The data is cloned as if using [`postMessage()`][`port.postMessage()`],
according to the [HTML structured clone algorithm][]. according to the [HTML structured clone algorithm][].
```js ```mjs
import { Worker, isMainThread, workerData } from 'node:worker_threads';
if (isMainThread) {
const worker = new Worker(new URL(import.meta.url), { workerData: 'Hello, world!' });
} else {
console.log(workerData); // Prints 'Hello, world!'.
}
```
```cjs
'use strict';
const { Worker, isMainThread, workerData } = require('node:worker_threads'); const { Worker, isMainThread, workerData } = require('node:worker_threads');
if (isMainThread) { if (isMainThread) {
@ -580,7 +768,30 @@ changes:
Instances of `BroadcastChannel` allow asynchronous one-to-many communication Instances of `BroadcastChannel` allow asynchronous one-to-many communication
with all other `BroadcastChannel` instances bound to the same channel name. with all other `BroadcastChannel` instances bound to the same channel name.
```js ```mjs
import {
isMainThread,
BroadcastChannel,
Worker,
} from 'node:worker_threads';
const bc = new BroadcastChannel('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++)
new Worker(new URL(import.meta.url));
} else {
bc.postMessage('hello from every worker');
bc.close();
}
```
```cjs
'use strict'; 'use strict';
const { const {
@ -681,7 +892,18 @@ The `MessageChannel` has no methods of its own. `new MessageChannel()`
yields an object with `port1` and `port2` properties, which refer to linked yields an object with `port1` and `port2` properties, which refer to linked
[`MessagePort`][] instances. [`MessagePort`][] instances.
```js ```mjs
import { MessageChannel } from 'node:worker_threads';
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// Prints: received { foo: 'bar' } from the `port1.on('message')` listener
```
```cjs
'use strict';
const { MessageChannel } = require('node:worker_threads'); const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
@ -720,7 +942,23 @@ added: v10.5.0
The `'close'` event is emitted once either side of the channel has been The `'close'` event is emitted once either side of the channel has been
disconnected. disconnected.
```js ```mjs
import { MessageChannel } from 'node:worker_threads';
const { port1, port2 } = new MessageChannel();
// Prints:
// foobar
// closed!
port2.on('message', (message) => console.log(message));
port2.on('close', () => console.log('closed!'));
port1.postMessage('foobar');
port1.close();
```
```cjs
'use strict';
const { MessageChannel } = require('node:worker_threads'); const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
@ -841,7 +1079,21 @@ In particular, the significant differences to `JSON` are:
* {net.SocketAddress}es, * {net.SocketAddress}es,
* {X509Certificate}s. * {X509Certificate}s.
```js ```mjs
import { MessageChannel } from 'node:worker_threads';
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log(message));
const circularData = {};
circularData.foo = circularData;
// Prints: { foo: [Circular] }
port2.postMessage(circularData);
```
```cjs
'use strict';
const { MessageChannel } = require('node:worker_threads'); const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
@ -866,7 +1118,33 @@ from either thread. They cannot be listed in `transferList`.
`value` may still contain `ArrayBuffer` instances that are not in `value` may still contain `ArrayBuffer` instances that are not in
`transferList`; in that case, the underlying memory is copied rather than moved. `transferList`; in that case, the underlying memory is copied rather than moved.
```js ```mjs
import { MessageChannel } from 'node:worker_threads';
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log(message));
const uint8Array = new Uint8Array([ 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 = new Uint8Array(new SharedArrayBuffer(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 = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);
```
```cjs
'use strict';
const { MessageChannel } = require('node:worker_threads'); const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel(); const { port1, port2 } = new MessageChannel();
@ -1105,7 +1383,30 @@ See [`port.postMessage()`][] for more information on how messages are passed,
and what kind of JavaScript values can be successfully transported through and what kind of JavaScript values can be successfully transported through
the thread barrier. the thread barrier.
```js ```mjs
import assert from 'node:assert';
import {
Worker, MessageChannel, MessagePort, isMainThread, parentPort,
} from 'node:worker_threads';
if (isMainThread) {
const worker = new Worker(new URL(import.meta.url));
const subChannel = new MessageChannel();
worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
subChannel.port2.on('message', (value) => {
console.log('received:', value);
});
} else {
parentPort.once('message', (value) => {
assert(value.hereIsYourPort instanceof MessagePort);
value.hereIsYourPort.postMessage('the worker is sending this');
value.hereIsYourPort.close();
});
}
```
```cjs
'use strict';
const assert = require('node:assert'); const assert = require('node:assert');
const { const {
Worker, MessageChannel, MessagePort, isMainThread, parentPort, Worker, MessageChannel, MessagePort, isMainThread, parentPort,
@ -1391,7 +1692,29 @@ stuck in bootstrap. The following examples shows how the worker's entire
lifetime never accumulates any `idle` time, but is still be able to process lifetime never accumulates any `idle` time, but is still be able to process
messages. messages.
```js ```mjs
import { Worker, isMainThread, parentPort } from 'node:worker_threads';
if (isMainThread) {
const worker = new Worker(new URL(import.meta.url));
setInterval(() => {
worker.postMessage('hi');
console.log(worker.performance.eventLoopUtilization());
}, 100).unref();
} else {
parentPort.on('message', () => console.log('msg')).unref();
(function r(n) {
if (--n < 0) return;
const t = Date.now();
while (Date.now() - t < 300);
setImmediate(r, n);
})(10);
}
```
```cjs
'use strict';
const { Worker, isMainThread, parentPort } = require('node:worker_threads'); const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) { if (isMainThread) {
@ -1400,9 +1723,7 @@ if (isMainThread) {
worker.postMessage('hi'); worker.postMessage('hi');
console.log(worker.performance.eventLoopUtilization()); console.log(worker.performance.eventLoopUtilization());
}, 100).unref(); }, 100).unref();
return; } else {
}
parentPort.on('message', () => console.log('msg')).unref(); parentPort.on('message', () => console.log('msg')).unref();
(function r(n) { (function r(n) {
if (--n < 0) return; if (--n < 0) return;
@ -1410,6 +1731,7 @@ parentPort.on('message', () => console.log('msg')).unref();
while (Date.now() - t < 300); while (Date.now() - t < 300);
setImmediate(r, n); setImmediate(r, n);
})(10); })(10);
}
``` ```
The event loop utilization of a worker is available only after the [`'online'` The event loop utilization of a worker is available only after the [`'online'`