diff --git a/doc/api/globals.md b/doc/api/globals.md index cd6afed9626f..f7dfc4c59b4d 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1401,6 +1401,10 @@ Inside a worker, \[`worker_threads.parentPort`]\[] is the port behind `self.postMessage()` and the worker's `message` events, `isMainThread` is `false`, and `workerData` is `undefined`. +Because that thread keeps the event loop alive, `Worker` instances can be +mutated using the non-standard `process.ref(worker)` and `process.unref(worker)` +methods. + As a rule of thumb, use [`node:worker_threads`][] directly when a program needs `workerData`, a custom `env` or `execArgv`, resource limits, stdio redirection, the `'online'` and `'exit'` events, or `worker.threadId`; diff --git a/lib/internal/webworker.js b/lib/internal/webworker.js index bc7b55ed8ac1..e2a2086f9039 100644 --- a/lib/internal/webworker.js +++ b/lib/internal/webworker.js @@ -99,6 +99,8 @@ const { const kCurrentlyReceivingPorts = SymbolFor('nodejs.internal.kCurrentlyReceivingPorts'); +const kRef = SymbolFor('nodejs.ref'); +const kUnref = SymbolFor('nodejs.unref'); const kCreate = Symbol('kCreate'); const kInsidePort = Symbol('kInsidePort'); @@ -865,6 +867,17 @@ class Worker extends EventTarget { // arguments, and returned the same return value." this[kWorker]?.postMessage(message, transfer); } + + // The following properties are non-standard, Node.js extensions + [kRef]() { + validateThisInternalField(this, kWorker, 'Worker'); + this[kWorker]?.ref(); + } + + [kUnref]() { + validateThisInternalField(this, kWorker, 'Worker'); + this[kWorker]?.unref(); + } } ObjectDefineProperties(Worker.prototype, { diff --git a/test/fixtures/web-worker/echo.js b/test/fixtures/web-worker/echo.js new file mode 100644 index 000000000000..753c32599775 --- /dev/null +++ b/test/fixtures/web-worker/echo.js @@ -0,0 +1,5 @@ +'use strict'; + +addEventListener('message', (event) => { + postMessage(event.data); +}); diff --git a/test/parallel/test-webworker-ref-unref.js b/test/parallel/test-webworker-ref-unref.js new file mode 100644 index 000000000000..cff2bb5d097f --- /dev/null +++ b/test/parallel/test-webworker-ref-unref.js @@ -0,0 +1,29 @@ +// Flags: --experimental-web-worker +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +{ + // Both are no-ops on a worker whose script could not be loaded. + const worker = new Worker(fixtures.fileURL('web-worker', 'nonexistent.js').href); + worker.addEventListener('error', common.mustCall()); + process.unref(worker); + process.ref(worker); +} + +const worker = new Worker(fixtures.fileURL('web-worker', 'echo.js').href); + +worker.addEventListener('error', common.mustNotCall('worker failed')); +worker.addEventListener('message', common.mustCall(({ data }) => { + assert.strictEqual(data, 'hello'); + worker.terminate(); +})); + +process.once('beforeExit', common.mustCall(() => { + process.ref(worker); + worker.postMessage('hello'); +})); + +process.unref(worker);