Рабочие потоки
[Стабильно: 2 - Стабильно]
Стабильно: 2 Стабильность: 2 - Стабильно
Исходный код: lib/worker_threads.js
Модуль node:worker_threads
позволяет использовать потоки, которые выполняют JavaScript параллельно. Для доступа к нему:
const worker = require('node:worker_threads')
Рабочие потоки полезны для выполнения ресурсоемких операций JavaScript. Они не сильно помогают в работе с интенсивным вводом-выводом. Встроенные асинхронные операции ввода-вывода Node.js более эффективны, чем рабочие потоки.
В отличие от child_process
или cluster
, worker_threads
могут совместно использовать память. Они делают это путем передачи экземпляров ArrayBuffer
или совместного использования экземпляров SharedArrayBuffer
.
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
автоматически получает свою собственную копию данных среды.
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
.
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
, таким образом.
Эта операция не может быть отменена.
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
Проверяет, помечен ли объект как непередаваемый с помощью markAsUntransferable()
.
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
объекты.
Эту операцию нельзя отменить.
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
port
<MessagePort> Порт сообщений для передачи.contextifiedSandbox
<Object> Контекстуализированный объект, возвращаемый методомvm.createContext()
.- Возвращает: <MessagePort>
Переносит 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')
.
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 вложенных потоков, последний из которых попытается связаться с основным потоком.
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
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 |
port
<MessagePort> | <BroadcastChannel>- Возвращает: <Object> | <undefined>
Получает одно сообщение из заданного MessagePort
. Если сообщение недоступно, возвращается undefined
, в противном случае возвращается объект с единственным свойством message
, содержащим полезную нагрузку сообщения, соответствующую самому старому сообщению в очереди MessagePort
.
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 должны совместно использовать доступ для чтения и записи к одному и тому же набору переменных среды.
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.
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
, связанными с тем же именем канала.
'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
.
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 |
- Расширяет: <EventTarget>
Экземпляры класса worker.MessagePort
представляют один конец асинхронного двунаправленного канала связи. Он может использоваться для передачи структурированных данных, областей памяти и других MessagePort
между различными Worker
ами.
Эта реализация соответствует браузерным MessagePort
.
Событие: 'close'
Добавлено в: v10.5.0
Событие 'close'
генерируется после отключения одной из сторон канала.
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
error
<Error> Объект Error
Событие '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 |
value
<any>transferList
<Object[]>
Отправляет значение JavaScript на принимающую сторону этого канала. value
передается способом, совместимым с алгоритмом структурированного клонирования HTML.
В частности, существенные отличия от JSON
:
value
может содержать циклические ссылки.value
может содержать экземпляры встроенных типов JS, таких какRegExp
,BigInt
,Map
,Set
и т. д.value
может содержать типизированные массивы, как с использованиемArrayBuffer
, так иSharedArrayBuffer
.value
может содержать экземплярыWebAssembly.Module
.value
не может содержать собственные (поддерживаемые C++) объекты, кроме:
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
; в этом случае происходит копирование, а не перемещение, базовой памяти.
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
, становятся непригодными для использования.
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.
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
:
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 - Экспериментальная
- Возвращает: <boolean>
Если значение 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
- Расширяет: <EventEmitter>
Класс 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 могут быть успешно переданы через границу потока.
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. Должен быть либо абсолютным путём, либо относительным путём (т.е. относительно текущего рабочего каталога), начинающимся с./
или../
, либо объектом WHATWGURL
с использованием протокола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, аналогично другим ресурсам, таким как сетевые сокеты или дескрипторы файлов, управляемые через APIFileHandle
. Этот параметр автоматически наследуется всеми вложенными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
err
<Error>
Событие 'error'
генерируется, если поток worker генерирует неперехваченное исключение. В этом случае worker завершается.
Событие: 'exit'
Добавлено в: v10.5.0
exitCode
<integer>
Событие '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
error
<Error> Объект Error
Событие '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 |
Возвращает читаемый поток для снимка 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
, но все еще способен обрабатывать сообщения.
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
value
<any>transferList
<Object[]>
Отправляет сообщение воркеру, которое принимается через 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 |
- Возвращает: <Promise>
Остановить все выполнение 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.
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')
}
'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, каждый порожденный поток будет порождать другой, пока приложение не завершится аварийно.