Skip to content

Рабочие потоки

[Стабильно: 2 - Стабильно]

Стабильно: 2 Стабильность: 2 - Стабильно

Исходный код: lib/worker_threads.js

Модуль node:worker_threads позволяет использовать потоки, которые выполняют JavaScript параллельно. Для доступа к нему:

js
const worker = require('node:worker_threads')

Рабочие потоки полезны для выполнения ресурсоемких операций JavaScript. Они не сильно помогают в работе с интенсивным вводом-выводом. Встроенные асинхронные операции ввода-вывода Node.js более эффективны, чем рабочие потоки.

В отличие от child_process или cluster, worker_threads могут совместно использовать память. Они делают это путем передачи экземпляров ArrayBuffer или совместного использования экземпляров SharedArrayBuffer.

js
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads')

if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script,
      })
      worker.on('message', resolve)
      worker.on('error', reject)
      worker.on('exit', code => {
        if (code !== 0) reject(new Error(`Рабочий поток завершился с кодом выхода ${code}`))
      })
    })
  }
} else {
  const { parse } = require('some-js-parsing-library')
  const script = workerData
  parentPort.postMessage(parse(script))
}

В приведенном выше примере создается поток Worker для каждого вызова parseJSAsync(). На практике для таких задач используйте пул рабочих потоков. В противном случае накладные расходы на создание рабочих потоков, вероятно, превысят их преимущества.

При реализации пула рабочих потоков используйте API AsyncResource, чтобы информировать диагностические инструменты (например, для предоставления асинхронных трассировок стека) о корреляции между задачами и их результатами. См. "Использование AsyncResource для пула потоков Worker" в документации по async_hooks для примера реализации.

Рабочие потоки по умолчанию наследуют параметры, не относящиеся к процессу. Обратитесь к Параметры конструктора Worker, чтобы узнать, как настроить параметры рабочих потоков, в частности параметры argv и execArgv.

worker.getEnvironmentData(key)

[История]

ВерсияИзменения
v17.5.0, v16.15.0Больше не экспериментальная.
v15.12.0, v14.18.0Добавлена в: v15.12.0, v14.18.0
  • key <любое> Любое произвольное, клонируемое значение JavaScript, которое может использоваться в качестве ключа <Map>.
  • Возвращает: <любое>

Внутри потока worker worker.getEnvironmentData() возвращает клон данных, переданных порождающему потоку worker.setEnvironmentData(). Каждый новый Worker автоматически получает свою собственную копию данных среды.

js
const { Worker, isMainThread, setEnvironmentData, getEnvironmentData } = require('node:worker_threads')

if (isMainThread) {
  setEnvironmentData('Hello', 'World!')
  const worker = new Worker(__filename)
} else {
  console.log(getEnvironmentData('Hello')) // Выведет 'World!'.
}

worker.isMainThread

Добавлено в: v10.5.0

Равно true, если этот код не выполняется внутри потока Worker.

js
const { Worker, isMainThread } = require('node:worker_threads')

if (isMainThread) {
  // Это перезагружает текущий файл внутри экземпляра Worker.
  new Worker(__filename)
} else {
  console.log('Inside Worker!')
  console.log(isMainThread) // Выведет 'false'.
}

worker.markAsUntransferable(object)

Добавлено в: v14.5.0, v12.19.0

  • object <любое> Любое произвольное значение JavaScript.

Помечает объект как непередаваемый. Если object встречается в списке передачи вызова port.postMessage(), возникает ошибка. Это не имеет эффекта, если object является примитивным значением.

В частности, это имеет смысл для объектов, которые могут быть клонированы, а не переданы, и которые используются другими объектами на стороне отправителя. Например, Node.js помечает ArrayBufferы, которые он использует для своего пула Buffer, таким образом.

Эта операция не может быть отменена.

js
const { MessageChannel, markAsUntransferable } = require('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 {
  // Это вызовет ошибку, потому что pooledBuffer непередаваемый.
  port1.postMessage(typedArray1, [typedArray1.buffer])
} catch (error) {
  // error.name === 'DataCloneError'
}

// Следующая строка выведет содержимое typedArray1 -- он всё ещё владеет
// своей памятью и не был передан. Без
// `markAsUntransferable()`, это вывело бы пустой Uint8Array, и
// вызов postMessage был бы успешным.
// typedArray2 также нетронут.
console.log(typedArray1)
console.log(typedArray2)

В браузерах нет эквивалента этому API.

worker.isMarkedAsUntransferable(object)

Добавлено в: v21.0.0

  • object <any> Любое значение JavaScript.
  • Возвращает: <boolean>

Проверяет, помечен ли объект как непередаваемый с помощью markAsUntransferable().

js
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads')

const pooledBuffer = new ArrayBuffer(8)
markAsUntransferable(pooledBuffer)

isMarkedAsUntransferable(pooledBuffer) // Возвращает true.

В браузерах нет эквивалента этому API.

worker.markAsUncloneable(object)

Добавлено в: v23.0.0

  • object <any> Любое произвольное значение JavaScript.

Помечает объект как неклонируемый. Если object используется как message в вызове port.postMessage(), возникает ошибка. Это не имеет эффекта, если object является примитивным значением.

Это не влияет на ArrayBuffer или любые похожие на Buffer объекты.

Эту операцию нельзя отменить.

js
const { markAsUncloneable } = require('node:worker_threads')

const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
  // Это вызовет ошибку, потому что anyObject не клонируем.
  port1.postMessage(anyObject)
} catch (error) {
  // error.name === 'DataCloneError'
}

В браузерах нет эквивалента этому API.

worker.moveMessagePortToContext(port, contextifiedSandbox)

Добавлено в: v11.13.0

Переносит MessagePort в другой контекст vm. Исходный объект port становится непригодным для использования, и возвращенный экземпляр MessagePort занимает его место.

Возвращаемый MessagePort является объектом в целевом контексте и наследует от его глобального класса Object. Объекты, передаваемые в обработчик port.onmessage(), также создаются в целевом контексте и наследуют от его глобального класса Object.

Однако созданный MessagePort больше не наследует от EventTarget, и только port.onmessage() может использоваться для приема событий с его помощью.

worker.parentPort

Добавлено в: v10.5.0

Если этот поток является Worker, это MessagePort, позволяющий общаться с родительским потоком. Сообщения, отправленные с использованием parentPort.postMessage(), доступны в родительском потоке с использованием worker.on('message'), а сообщения, отправленные из родительского потока с использованием worker.postMessage(), доступны в этом потоке с использованием parentPort.on('message').

js
const { Worker, isMainThread, parentPort } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename)
  worker.once('message', message => {
    console.log(message) // Выведет 'Hello, world!'.
  })
  worker.postMessage('Hello, world!')
} else {
  // Когда получено сообщение из родительского потока, отправьте его обратно:
  parentPort.once('message', message => {
    parentPort.postMessage(message)
  })
}

worker.postMessageToThread(threadId, value[, transferList][, timeout])

Добавлено в: v22.5.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1.1 - Активная разработка

  • threadId <number> Идентификатор целевого потока. Если идентификатор потока неверен, будет выброшена ошибка ERR_WORKER_MESSAGING_FAILED. Если идентификатор целевого потока совпадает с текущим идентификатором потока, будет выброшена ошибка ERR_WORKER_MESSAGING_SAME_THREAD.
  • value <any> Значение для отправки.
  • transferList <Object[]> Если один или несколько объектов, подобных MessagePort, передаются в value, для этих элементов требуется transferList, иначе будет выброшена ошибка ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. Дополнительную информацию см. в разделе port.postMessage().
  • timeout <number> Время ожидания доставки сообщения в миллисекундах. По умолчанию это undefined, что означает бесконечное ожидание. Если операция завершается по таймауту, будет выброшена ошибка ERR_WORKER_MESSAGING_TIMEOUT.
  • Возвращает: <Promise> Promise, который выполняется, если сообщение было успешно обработано целевым потоком.

Отправляет значение другому рабочему процессу, идентифицированному по его идентификатору потока.

Если целевой поток не имеет обработчика события workerMessage, то операция выбросит ошибку ERR_WORKER_MESSAGING_FAILED.

Если целевой поток выбросил ошибку при обработке события workerMessage, то операция выбросит ошибку ERR_WORKER_MESSAGING_ERRORED.

Этот метод следует использовать, когда целевой поток не является прямым родителем или потомком текущего потока. Если два потока являются родительско-дочерними, используйте require('node:worker_threads').parentPort.postMessage() и worker.postMessage(), чтобы позволить потокам общаться.

В примере ниже показано использование postMessageToThread: он создает 10 вложенных потоков, последний из которых попытается связаться с основным потоком.

js
import { fileURLToPath } from 'node:url'
import process from 'node:process'
import { postMessageToThread, threadId, workerData, Worker } from 'node:worker_threads'

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(fileURLToPath(import.meta.url), {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    channel.postMessage('done')
    channel.close()
  })

  await postMessageToThread(0, { message: 'ping' })
}

channel.onmessage = channel.close
js
const { postMessageToThread, threadId, workerData, Worker } = require('node:worker_threads')

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(__filename, {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (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.receiveMessageOnPort(port)

[История]

ВерсияИзменения
v15.12.0Аргумент port теперь также может ссылаться на BroadcastChannel.
v12.3.0Добавлено в: v12.3.0

Получает одно сообщение из заданного MessagePort. Если сообщение недоступно, возвращается undefined, в противном случае возвращается объект с единственным свойством message, содержащим полезную нагрузку сообщения, соответствующую самому старому сообщению в очереди MessagePort.

js
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.postMessage({ hello: 'world' })

console.log(receiveMessageOnPort(port2))
// Выводит: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2))
// Выводит: undefined

При использовании этой функции событие 'message' не генерируется, и обработчик onmessage не вызывается.

worker.resourceLimits

Добавлено в: v13.2.0, v12.16.0

Предоставляет набор ограничений ресурсов JS-движка внутри этого потока Worker. Если параметр resourceLimits был передан конструктору Worker, это соответствует его значениям.

Если это используется в основном потоке, его значение представляет собой пустой объект.

worker.SHARE_ENV

Добавлено в: v11.14.0

Специальное значение, которое может быть передано в качестве параметра env конструктора Worker, чтобы указать, что текущий поток и поток Worker должны совместно использовать доступ для чтения и записи к одному и тому же набору переменных среды.

js
const { Worker, SHARE_ENV } = require('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) // Выведет 'foo'.
})

worker.setEnvironmentData(key[, value])

[История]

ВерсияИзменения
v17.5.0, v16.15.0Больше не экспериментальный.
v15.12.0, v14.18.0Добавлено в: v15.12.0, v14.18.0
  • key <любое> Любое произвольное, клонируемое значение JavaScript, которое может использоваться в качестве ключа <Map>.
  • value <любое> Любое произвольное, клонируемое значение JavaScript, которое будет клонировано и автоматически передано всем новым экземплярам Worker. Если value передается как undefined, любое ранее установленное значение для key будет удалено.

API worker.setEnvironmentData() устанавливает содержимое worker.getEnvironmentData() в текущем потоке и всех новых экземплярах Worker, порожденных из текущего контекста.

worker.threadId

Добавлено в: v10.5.0

Целочисленный идентификатор текущего потока. В соответствующем объекте worker (если таковой имеется), он доступен как worker.threadId. Это значение уникально для каждого экземпляра Worker внутри одного процесса.

worker.workerData

Добавлен в: v10.5.0

Произвольное значение JavaScript, содержащее клон данных, переданных в конструктор Worker этого потока.

Данные клонируются так же, как при использовании postMessage(), в соответствии с алгоритмом структурированного клонирования HTML.

js
const { Worker, isMainThread, workerData } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: 'Hello, world!' })
} else {
  console.log(workerData) // Выводит 'Hello, world!'.
}

Class: BroadcastChannel extends EventTarget

[История]

ВерсияИзменения
v18.0.0Больше не экспериментальный.
v15.4.0Добавлен в: v15.4.0

Экземпляры BroadcastChannel позволяют осуществлять асинхронную связь один-ко-многим со всеми другими экземплярами BroadcastChannel, связанными с тем же именем канала.

js
'use strict'

const { isMainThread, BroadcastChannel, Worker } = require('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(__filename)
} else {
  bc.postMessage('hello from every worker')
  bc.close()
}

new BroadcastChannel(name)

Добавлен в: v15.4.0

  • name <любое> Имя канала для подключения. Допускается любое значение JavaScript, которое может быть преобразовано в строку с помощью ${name}.

broadcastChannel.close()

Добавлен в: v15.4.0

Закрывает соединение BroadcastChannel.

broadcastChannel.onmessage

Добавлен в: v15.4.0

  • Тип: <Функция> Вызывается с одним аргументом MessageEvent, когда получено сообщение.

broadcastChannel.onmessageerror

Добавлено в: v15.4.0

  • Тип: <Function> Вызывается, если полученное сообщение не может быть десериализовано.

broadcastChannel.postMessage(message)

Добавлено в: v15.4.0

  • message <any> Любое клонируемое значение JavaScript.

broadcastChannel.ref()

Добавлено в: v15.4.0

Противоположность unref(). Вызов ref() для ранее unref()ed BroadcastChannel не позволяет программе завершиться, если это единственная активная дескриптор (поведение по умолчанию). Если порт ref()ed, повторный вызов ref() не оказывает никакого эффекта.

broadcastChannel.unref()

Добавлено в: v15.4.0

Вызов unref() для BroadcastChannel позволяет потоку завершиться, если это единственная активная дескриптор в системе событий. Если BroadcastChannel уже unref()ed, повторный вызов unref() не оказывает никакого эффекта.

Класс: MessageChannel

Добавлено в: v10.5.0

Экземпляры класса worker.MessageChannel представляют собой асинхронный двунаправленный канал связи. MessageChannel не имеет собственных методов. new MessageChannel() возвращает объект со свойствами port1 и port2, которые ссылаются на связанные экземпляры MessagePort.

js
const { MessageChannel } = require('node:worker_threads')

const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('received', message))
port2.postMessage({ foo: 'bar' })
// Выводит: received { foo: 'bar' } из обработчика `port1.on('message')`

Класс: MessagePort

[История]

ВерсияИзменения
v14.7.0Этот класс теперь наследуется от EventTarget, а не от EventEmitter.
v10.5.0Добавлено в: v10.5.0

Экземпляры класса worker.MessagePort представляют один конец асинхронного двунаправленного канала связи. Он может использоваться для передачи структурированных данных, областей памяти и других MessagePort между различными Workerами.

Эта реализация соответствует браузерным MessagePort.

Событие: 'close'

Добавлено в: v10.5.0

Событие 'close' генерируется после отключения одной из сторон канала.

js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

// Выводит:
//   foobar
//   closed!
port2.on('message', message => console.log(message))
port2.on('close', () => console.log('closed!'))

port1.postMessage('foobar')
port1.close()

Событие: 'message'

Добавлено в: v10.5.0

  • value <any> Передаваемое значение

Событие 'message' генерируется для любого входящего сообщения, содержащего клонированный входной параметр port.postMessage().

Слушатели этого события получают клон параметра value, переданного в postMessage(), и никаких других аргументов.

Событие: 'messageerror'

Добавлено в: v14.5.0, v12.19.0

Событие 'messageerror' генерируется при ошибке десериализации сообщения.

В настоящее время это событие генерируется, когда происходит ошибка при создании отправленного объекта JS на принимающей стороне. Такие ситуации редки, но могут возникнуть, например, когда определенные объекты API Node.js принимаются в vm.Context (где API Node.js в настоящее время недоступны).

port.close()

Добавлено в: v10.5.0

Отключает дальнейшую отправку сообщений с любой стороны соединения. Этот метод можно вызывать, когда дальнейшая связь через этот MessagePort не требуется.

Событие 'close' генерируется для обоих экземпляров MessagePort, которые являются частью канала.

port.postMessage(value[, transferList])

[История]

ВерсияИзменения
v21.0.0Выбрасывается ошибка, если в списке передаваемых объектов находится непередаваемый объект.
v15.6.0Добавлен X509Certificate в список клонируемых типов.
v15.0.0Добавлен CryptoKey в список клонируемых типов.
v15.14.0, v14.18.0Добавлен 'BlockList' в список клонируемых типов.
v15.9.0, v14.18.0Добавлены типы 'Histogram' в список клонируемых типов.
v14.5.0, v12.19.0Добавлен KeyObject в список клонируемых типов.
v14.5.0, v12.19.0Добавлен FileHandle в список передаваемых типов.
v10.5.0Добавлено в: v10.5.0

Отправляет значение JavaScript на принимающую сторону этого канала. value передается способом, совместимым с алгоритмом структурированного клонирования HTML.

В частности, существенные отличия от JSON:

  • value может содержать циклические ссылки.
  • value может содержать экземпляры встроенных типов JS, таких как RegExp, BigInt, Map, Set и т. д.
  • value может содержать типизированные массивы, как с использованием ArrayBuffer, так и SharedArrayBuffer.
  • value может содержать экземпляры WebAssembly.Module.
  • value не может содержать собственные (поддерживаемые C++) объекты, кроме:
js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

port1.on('message', message => console.log(message))

const circularData = {}
circularData.foo = circularData
// Выводит: { foo: [Circular] }
port2.postMessage(circularData)

transferList может быть списком объектов ArrayBuffer, MessagePort и FileHandle. После передачи они больше не могут использоваться на отправляющей стороне канала (даже если они не содержатся в value). В отличие от дочерних процессов, передача дескрипторов, таких как сетевые сокеты, в настоящее время не поддерживается.

Если value содержит экземпляры SharedArrayBuffer, то они доступны из любого потока. Их нельзя указывать в transferList.

value все еще может содержать экземпляры ArrayBuffer, которые не находятся в transferList; в этом случае происходит копирование, а не перемещение, базовой памяти.

js
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()

port1.on('message', message => console.log(message))

const uint8Array = new Uint8Array([1, 2, 3, 4])
// Это отправляет копию `uint8Array`:
port2.postMessage(uint8Array)
// Это не копирует данные, но делает `uint8Array` непригодным для использования:
port2.postMessage(uint8Array, [uint8Array.buffer])

// Память для `sharedUint8Array` доступна как из оригинала, так и из копии, полученной через `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)

// Это передаёт новосозданный порт сообщения получателю.
// Это может быть использовано, например, для создания каналов связи между
// несколькими потоками `Worker`, которые являются потомками одного родительского потока.
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])

Объект сообщения клонируется немедленно, и его можно изменить после отправки без побочных эффектов.

Для получения дополнительной информации о механизмах сериализации и десериализации, лежащих в основе этого API, см. API сериализации модуля node:v8.

Аспекты передачи TypedArrays и Buffers

Все экземпляры TypedArray и Buffer являются представлениями базового ArrayBuffer. То есть именно ArrayBuffer фактически хранит необработанные данные, в то время как объекты TypedArray и Buffer предоставляют способ просмотра и манипулирования этими данными. Возможно и распространено создание нескольких представлений для одного и того же экземпляра ArrayBuffer. Необходимо соблюдать осторожность при использовании списка передачи для передачи ArrayBuffer, поскольку это приводит к тому, что все экземпляры TypedArray и Buffer, которые используют один и тот же ArrayBuffer, становятся непригодными для использования.

js
const ab = new ArrayBuffer(10)

const u1 = new Uint8Array(ab)
const u2 = new Uint16Array(ab)

console.log(u2.length) // выведет 5

port.postMessage(u1, [u1.buffer])

console.log(u2.length) // выведет 0

Для экземпляров Buffer, в частности, возможность передачи или клонирования базового ArrayBuffer полностью зависит от того, как были созданы экземпляры, что часто невозможно достоверно определить.

ArrayBuffer может быть помечен с помощью markAsUntransferable(), чтобы указать, что он всегда должен клонироваться, а не передаваться.

В зависимости от того, как был создан экземпляр Buffer, он может владеть или не владеть своим базовым ArrayBuffer. ArrayBuffer не должен передаваться, если не известно, что экземпляр Buffer им владеет. В частности, для Buffer-ов, созданных из внутреннего пула Buffer (например, с помощью Buffer.from() или Buffer.allocUnsafe()), их передача невозможна, и они всегда клонируются, что отправляет копию всего пула Buffer. Это поведение может привести к непреднамеренному увеличению потребления памяти и возможным проблемам безопасности.

См. Buffer.allocUnsafe() для получения дополнительной информации о пуле Buffer.

ArrayBuffer для экземпляров Buffer, созданных с помощью Buffer.alloc() или Buffer.allocUnsafeSlow(), всегда могут быть переданы, но это делает все другие существующие представления этих ArrayBuffer непригодными для использования.

Аспекты клонирования объектов с прототипами, классами и аксессорами

Поскольку клонирование объектов использует алгоритм структурированного клонирования HTML, неперечисляемые свойства, аксессоры свойств и прототипы объектов не сохраняются. В частности, объекты Buffer будут считаться простыми Uint8Array на принимающей стороне, а экземпляры классов JavaScript будут клонироваться как простые объекты JavaScript.

js
const b = Symbol('b')

class Foo {
  #a = 1
  constructor() {
    this[b] = 2
    this.c = 3
  }

  get d() {
    return 4
  }
}

const { port1, port2 } = new MessageChannel()

port1.onmessage = ({ data }) => console.log(data)

port2.postMessage(new Foo())

// Выведет: { c: 3 }

Это ограничение распространяется на многие встроенные объекты, такие как глобальный объект URL:

js
const { port1, port2 } = new MessageChannel()

port1.onmessage = ({ data }) => console.log(data)

port2.postMessage(new URL('https://example.org'))

// Выведет: { }

port.hasRef()

Добавлено в: v18.1.0, v16.17.0

[Стабильная версия: 1 - Экспериментальная]

Стабильная версия: 1 Стабильность: 1 - Экспериментальная

Если значение true, объект MessagePort будет поддерживать активность цикла событий Node.js.

port.ref()

Добавлено в: v10.5.0

Противоположность unref(). Вызов ref() для порта, ранее обработанного unref(), не позволяет программе завершиться, если это единственная активная обработка (поведение по умолчанию). Если порт имеет ref(), повторный вызов ref() не оказывает никакого эффекта.

Если слушатели добавляются или удаляются с помощью .on('message'), порт автоматически переходит в состояние ref() и unref() в зависимости от того, существуют ли слушатели для события.

port.start()

Добавлено в: v10.5.0

Начинает прием сообщений на этом MessagePort. При использовании этого порта в качестве излучателя событий, этот метод вызывается автоматически после подключения прослушивателей 'message'.

Этот метод существует для соответствия с веб-API MessagePort. В Node.js он полезен только для игнорирования сообщений, когда отсутствует обработчик событий. Node.js также отличается в обработке .onmessage. Его установка автоматически вызывает .start(), но его отмена позволяет сообщениям накапливаться в очереди до тех пор, пока не будет установлен новый обработчик или порт не будет удален.

port.unref()

Добавлено в: v10.5.0

Вызов unref() для порта позволяет потоку завершиться, если это единственный активный дескриптор в системе событий. Если порт уже unref()ed, повторный вызов unref() не оказывает никакого эффекта.

Если прослушиватели добавляются или удаляются с помощью .on('message'), порт автоматически ref()ed и unref()ed в зависимости от того, существуют ли прослушиватели для события.

Класс: Worker

Добавлено в: v10.5.0

Класс Worker представляет собой независимый поток выполнения JavaScript. Внутри него доступны большинство API Node.js.

Заметные отличия в среде Worker:

  • Потоки process.stdin, process.stdout и process.stderr могут быть перенаправлены родительским потоком.
  • Свойство require('node:worker_threads').isMainThread установлено в false.
  • Доступен порт сообщений require('node:worker_threads').parentPort.
  • process.exit() не останавливает всю программу, а только отдельный поток, и process.abort() недоступен.
  • process.chdir() и методы process, устанавливающие идентификаторы групп или пользователей, недоступны.
  • process.env является копией переменных среды родительского потока, если не указано иное. Изменения в одной копии не видны в других потоках и не видны для нативных надстроек (если только worker.SHARE_ENV не передается как параметр env в конструктор Worker). В Windows, в отличие от основного потока, копия переменных среды работает в режиме чувствительности к регистру.
  • process.title нельзя изменить.
  • Сигналы не передаются через process.on('...').
  • Выполнение может остановиться в любой момент в результате вызова worker.terminate().
  • Каналы IPC от родительских процессов недоступны.
  • Модуль trace_events не поддерживается.
  • Нативные надстройки могут быть загружены из нескольких потоков только в том случае, если они соответствуют определенным условиям.

Создание экземпляров Worker внутри других Worker возможно.

Как и Web Workers и модуль node:cluster, двусторонняя связь может быть достигнута с помощью обмена сообщениями между потоками. Внутренне Worker имеет встроенную пару MessagePort, которые уже связаны друг с другом при создании Worker. Хотя объект MessagePort на стороне родителя не предоставляется напрямую, его функциональность предоставляется через worker.postMessage() и событие worker.on('message') в объекте Worker для родительского потока.

Для создания пользовательских каналов обмена сообщениями (что рекомендуется вместо использования глобального канала по умолчанию, поскольку это облегчает разделение ответственности), пользователи могут создать объект MessageChannel в любом из потоков и передать один из MessagePort этого MessageChannel другому потоку через существующий канал, например, глобальный.

См. port.postMessage() для получения дополнительной информации о том, как передаются сообщения и какие значения JavaScript могут быть успешно переданы через границу потока.

js
const assert = require('node:assert')
const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('node:worker_threads')
if (isMainThread) {
  const worker = new Worker(__filename)
  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()
  })
}

new Worker(filename[, options])

[История]

ВерсияИзменения
v19.8.0, v18.16.0Добавлена поддержка параметра name, позволяющего добавлять имя к заголовку worker для отладки.
v14.9.0Параметр filename может быть объектом WHATWG URL с использованием протокола data:.
v14.9.0Параметр trackUnmanagedFds был установлен в true по умолчанию.
v14.6.0, v12.19.0Введён параметр trackUnmanagedFds.
v13.13.0, v12.17.0Введён параметр transferList.
v13.12.0, v12.17.0Параметр filename может быть объектом WHATWG URL с использованием протокола file:.
v13.4.0, v12.16.0Введён параметр argv.
v13.2.0, v12.16.0Введён параметр resourceLimits.
v10.5.0Добавлено в: v10.5.0
  • filename <строка> | <URL> Путь к главному скрипту или модулю Worker. Должен быть либо абсолютным путём, либо относительным путём (т.е. относительно текущего рабочего каталога), начинающимся с ./ или ../, либо объектом WHATWG URL с использованием протокола file: или data:. При использовании URL data: данные интерпретируются на основе типа MIME с использованием загрузчика модулей ECMAScript. Если options.eval равно true, это строка, содержащая код JavaScript, а не путь.

  • options <Объект>

    • argv <any[]> Список аргументов, которые будут преобразованы в строку и добавлены к process.argv в worker. Это в основном аналогично workerData, но значения доступны в глобальном process.argv, как если бы они были переданы в качестве параметров командной строки в скрипт.

    • env <Объект> Если задано, указывает начальное значение process.env внутри потока Worker. В качестве специального значения можно использовать worker.SHARE_ENV, чтобы указать, что родительский поток и дочерний поток должны совместно использовать свои переменные среды; в этом случае изменения объекта process.env одного потока влияют и на другой поток. По умолчанию: process.env.

    • eval <булево> Если true и первый аргумент является строкой, интерпретирует первый аргумент конструктора как скрипт, который выполняется после того, как worker станет доступен.

    • execArgv <строка[]> Список параметров командной строки Node, передаваемых worker. Параметры V8 (такие как --max-old-space-size) и параметры, влияющие на процесс (такие как --title), не поддерживаются. Если задано, это предоставляется как process.execArgv внутри worker. По умолчанию параметры наследуются от родительского потока.

    • stdin <булево> Если это установлено в true, то worker.stdin предоставляет доступный для записи поток, содержимое которого отображается как process.stdin внутри Worker. По умолчанию данные не предоставляются.

    • stdout <булево> Если это установлено в true, то worker.stdout автоматически не передаётся в process.stdout в родительском процессе.

    • stderr <булево> Если это установлено в true, то worker.stderr автоматически не передаётся в process.stderr в родительском процессе.

    • workerData <любое> Любое значение JavaScript, которое клонируется и становится доступным как require('node:worker_threads').workerData. Клонирование происходит, как описано в алгоритме структурированного клонирования HTML, и возникает ошибка, если объект не может быть клонирован (например, потому что он содержит функции).

    • trackUnmanagedFds <булево> Если это установлено в true, то Worker отслеживает необработанные дескрипторы файлов, управляемые через fs.open() и fs.close(), и закрывает их при выходе Worker, аналогично другим ресурсам, таким как сетевые сокеты или дескрипторы файлов, управляемые через API FileHandle. Этот параметр автоматически наследуется всеми вложенными Worker. По умолчанию: true.

    • transferList <Объект[]> Если один или несколько объектов типа MessagePort передаются в workerData, для этих элементов требуется transferList, иначе возникает ошибка ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. См. port.postMessage() для получения дополнительной информации.

    • resourceLimits <Объект> Необязательный набор ограничений ресурсов для нового экземпляра JS-движка. Достижение этих ограничений приводит к завершению работы экземпляра Worker. Эти ограничения влияют только на JS-движок и не на внешние данные, включая ArrayBuffer. Даже если эти ограничения установлены, процесс может все еще аварийно завершиться, если он столкнется с глобальной ситуацией нехватки памяти.

    • maxOldGenerationSizeMb <число> Максимальный размер основной кучи в МБ. Если установлен аргумент командной строки --max-old-space-size, он переопределяет это значение.

    • maxYoungGenerationSizeMb <число> Максимальный размер пространства кучи для недавно созданных объектов. Если установлен аргумент командной строки --max-semi-space-size, он переопределяет это значение.

    • codeRangeSizeMb <число> Размер предварительно выделенного диапазона памяти, используемого для сгенерированного кода.

    • stackSizeMb <число> Максимальный размер стека по умолчанию для потока. Малые значения могут привести к неработоспособным экземплярам Worker. По умолчанию: 4.

    • name <строка> Необязательное name, которое будет добавлено к заголовку worker для целей отладки/идентификации, формируя окончательный заголовок как [worker ${id}] ${name}. По умолчанию: ''.

Событие: 'error'

Добавлено в: v10.5.0

Событие 'error' генерируется, если поток worker генерирует неперехваченное исключение. В этом случае worker завершается.

Событие: 'exit'

Добавлено в: v10.5.0

Событие 'exit' генерируется после остановки worker. Если worker завершился вызовом process.exit(), параметр exitCode — это переданный код выхода. Если worker был завершен принудительно, параметр exitCode равен 1.

Это последнее событие, генерируемое любым экземпляром Worker.

Событие: 'message'

Добавлено в: v10.5.0

  • value <any> Переданное значение

Событие 'message' генерируется, когда поток worker вызвал require('node:worker_threads').parentPort.postMessage(). См. событие port.on('message') для получения более подробной информации.

Все сообщения, отправленные из потока worker, генерируются до того, как событие 'exit' сгенерируется для объекта Worker.

Событие: 'messageerror'

Добавлено в: v14.5.0, v12.19.0

Событие 'messageerror' генерируется при неудачной десериализации сообщения.

Событие: 'online'

Добавлено в: v10.5.0

Событие 'online' генерируется, когда поток worker начал выполнение кода JavaScript.

worker.getHeapSnapshot([options])

[История]

ВерсияИзменения
v19.1.0Поддержка параметров для настройки снимка кучи.
v13.9.0, v12.17.0Добавлено в: v13.9.0, v12.17.0
  • options <Object>

    • exposeInternals <boolean> Если true, то вывести внутренние данные в снимке кучи. По умолчанию: false.
    • exposeNumericValues <boolean> Если true, то вывести числовые значения в искусственных полях. По умолчанию: false.
  • Возвращает: <Promise> Promise для читаемого потока, содержащего снимок кучи V8

Возвращает читаемый поток для снимка V8 текущего состояния Worker. См. v8.getHeapSnapshot() для получения более подробной информации.

Если поток Worker больше не работает, что может произойти до генерации события 'exit', возвращаемый Promise отклоняется немедленно с ошибкой ERR_WORKER_NOT_RUNNING.

worker.performance

Добавлено в: v15.1.0, v14.17.0, v12.22.0

Объект, который можно использовать для запроса информации о производительности из экземпляра worker. Аналогично perf_hooks.performance.

performance.eventLoopUtilization([utilization1[, utilization2]])

Добавлено в: v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Object> Результат предыдущего вызова eventLoopUtilization().
  • utilization2 <Object> Результат предыдущего вызова eventLoopUtilization() до utilization1.
  • Возвращает: <Object>

Тот же вызов, что и perf_hooks eventLoopUtilization(), за исключением того, что возвращаются значения экземпляра worker.

Одно из отличий состоит в том, что в отличие от основного потока, загрузка в worker выполняется в цикле событий. Таким образом, использование цикла событий становится доступно сразу после начала выполнения скрипта worker.

Время idle, которое не увеличивается, не означает, что worker застрял в загрузке. Следующие примеры показывают, как за всю жизнь worker никогда не накапливает время idle, но все еще способен обрабатывать сообщения.

js
const { Worker, isMainThread, parentPort } = require('node:worker_threads')

if (isMainThread) {
  const worker = new Worker(__filename)
  setInterval(() => {
    worker.postMessage('hi')
    console.log(worker.performance.eventLoopUtilization())
  }, 100).unref()
  return
}

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)

Использование цикла событий worker доступно только после события 'online', и если вызов производится до этого события или после события 'exit', то все свойства имеют значение 0.

worker.postMessage(value[, transferList])

Добавлено в: v10.5.0

Отправляет сообщение воркеру, которое принимается через require('node:worker_threads').parentPort.on('message'). См. port.postMessage() для получения более подробной информации.

worker.ref()

Добавлено в: v10.5.0

Противоположность unref(). Вызов ref() для ранее unref()-нутого воркера не позволяет программе завершиться, если это единственная активная дескриптор (поведение по умолчанию). Если воркер находится в состоянии ref(), повторный вызов ref() не оказывает никакого эффекта.

worker.resourceLimits

Добавлено в: v13.2.0, v12.16.0

Предоставляет набор ограничений ресурсов движка JS для этого потока Worker. Если параметр resourceLimits был передан конструктору Worker, это соответствует его значениям.

Если воркер остановлен, возвращаемое значение представляет собой пустой объект.

worker.stderr

Добавлено в: v10.5.0

Это читаемый поток, который содержит данные, записанные в process.stderr внутри потока воркера. Если параметр stderr: true не был передан конструктору Worker, данные передаются в поток process.stderr родительского потока.

worker.stdin

Добавлено в: v10.5.0

Если stdin: true был передан в конструктор Worker, это записываемый поток. Данные, записанные в этот поток, будут доступны в потоке worker как process.stdin.

worker.stdout

Добавлено в: v10.5.0

Это читаемый поток, содержащий данные, записанные в process.stdout внутри потока worker. Если stdout: true не был передан в конструктор Worker, данные передаются в поток process.stdout родительского потока.

worker.terminate()

[История]

ВерсияИзменения
v12.5.0Эта функция теперь возвращает Promise. Передача обратного вызова устарела и была бесполезна до этой версии, поскольку Worker фактически завершался синхронно. Завершение теперь является полностью асинхронной операцией.
v10.5.0Добавлено в: v10.5.0

Остановить все выполнение JavaScript в потоке worker как можно скорее. Возвращает Promise для кода выхода, который выполняется, когда испускается событие 'exit'.

worker.threadId

Добавлено в: v10.5.0

Целочисленный идентификатор для ссылочного потока. Внутри потока worker он доступен как require('node:worker_threads').threadId. Это значение уникально для каждого экземпляра Worker в одном процессе.

worker.unref()

Добавлено в: v10.5.0

Вызов unref() для worker позволяет потоку завершиться, если это единственная активная дескрипторная запись в системе событий. Если worker уже unref()ed, повторный вызов unref() не оказывает никакого эффекта.

Заметки

Синхронная блокировка stdio

Workerы используют обмен сообщениями через <MessagePort> для реализации взаимодействия с stdio. Это означает, что вывод stdio, поступающий от Worker, может быть заблокирован синхронным кодом на принимающей стороне, который блокирует цикл событий Node.js.

js
import { Worker, isMainThread } from 'node:worker_threads'

if (isMainThread) {
  new Worker(new URL(import.meta.url))
  for (let n = 0; n < 1e10; n++) {
    // Цикл для имитации работы.
  }
} else {
  // Этот вывод будет заблокирован циклом for в главном потоке.
  console.log('foo')
}
js
'use strict'

const { Worker, isMainThread } = require('node:worker_threads')

if (isMainThread) {
  new Worker(__filename)
  for (let n = 0; n < 1e10; n++) {
    // Цикл для имитации работы.
  }
} else {
  // Этот вывод будет заблокирован циклом for в главном потоке.
  console.log('foo')
}

Запуск потоков worker из скриптов предварительной загрузки

Будьте осторожны при запуске потоков worker из скриптов предварительной загрузки (скрипты, загружаемые и запускаемые с помощью флага командной строки -r). Если параметр execArgv не задан явно, новые потоки Worker автоматически наследуют флаги командной строки от запущенного процесса и будут предварительно загружать те же скрипты предварительной загрузки, что и главный поток. Если скрипт предварительной загрузки безусловно запускает поток worker, каждый порожденный поток будет порождать другой, пока приложение не завершится аварийно.