Get to know MDN better
This page was translated from English by the community. Learn more and join the MDN Web Docs community.
Web Worker-ы предоставляют простое средство для запуска скриптов в фоновом потоке. Поток Worker'а может выполнять задачи без вмешательства в пользовательский интерфейс. К тому же, они могут осуществлять ввод/вывод, используя XMLHttpRequest (хотя атрибуты responseXML и channel всегда будут равны null). Существующий Worker может отсылать сообщения JavaScript коду-создателю через обработчик событий, указанный этим кодом (и наоборот). Эта статья даёт детальную инструкцию по использованию Web Workers.
Worker - это объект, создаваемый конструктором (например, Worker()) и запускающий именной JavaScript файл — этот файл содержит код, который будет выполнен в потоке Worker'а; объекты же Workers запускаются в другом глобальном контексте, отличающемся от текущего, - window. Поэтому использование переменной window для получения текущего глобального контекста (вместо self) внутри Worker вернёт ошибку.
Контекст Worker'а представлен объектом DedicatedWorkerGlobalScope в случае выделенных Workers (обычные Workers используются одним скриптом; совместные Workers используют объект SharedWorkerGlobalScope). Выделенный Worker доступен только из скрипта-родителя, в то время как совместные Workers могут быть доступны из нескольких сценариев.
Примечание: Смотрите страницу Web Workers API для справки по Workers и прочие руководства.
Вы можете запускать любой код внутри потока worker-а, за некоторыми исключениями. Например, вы не можете прямо манипулировать DOM внутри worker-а, или использовать некоторые методы по умолчанию и свойства объекта window. Но вы можете использовать большой набор опций, доступный под Window, включая WebSockets, и механизмы хранения данных, таких как IndexedDB и относящихся только к Firefox OS Data Store API. Для дополнительной информации смотрите Functions and classes available to workers.
Данные передаются между worker-ами и главным потоком через систему сообщений — обе стороны передают свои сообщения, используя метод postMessage() и отвечают на сообщения при помощи обработчика событий onmessage (сообщение хранится в атрибуте data события Message). Данные при этом копируются, а не делятся.
Объекты Workers могут, в свою очередь, создавать новые объекты workers, и так до тех пор, пока всё работает в рамках текущей страницы. Плюс к этому, объекты workers могут использовать XMLHttpRequest для сетевого ввода/вывода, но есть исключение - атрибуты responseXML и channel объекта XMLHttpRequest всегда возвращают null.
Как уже упоминалось выше, выделенный Worker доступен только для скрипта, который его вызвал. В этом разделе речь пойдёт о JavaScript, который можно найти в нашем основном примере выделенного Worker (запустить скрипт): этот пример позволяет ввести два числа для умножения. Эти числа отправляются в Worker, перемножаются, а результат возвращается на страницу и отображается.
Этот пример достаточно тривиален, но для ознакомления с базовыми концепциями worker-ов мы решили его упростить. Более продвинутые детали описаны далее в статье.
Для большего контроля над ошибками и обратной совместимости, рекомендуется обернуть ваш код доступа к worker-у в следующий (main.js):
Создание нового worker-а — это легко. Всё что вам нужно это вызвать конструктор Worker(), указав URI скрипта для выполнения в потоке worker-а (main.js):
Магия worker-ов происходит через postMessage() метод и обработчик событий onmessage. Когда вы хотите отправить сообщение в worker, вы доставляете сообщение к нему вот так (main.js):
В приведённом фрагменте кода мы имеем два <input> элемента, представленных переменными first и second; когда значение любой из переменных изменяется, myWorker.postMessage([first.value,second.value]) используется для отправки обоих значений, представленных в виде массива, в worker. Посредством аргумента message возможна передача практически любых данных в worker.
Внутри worker-a мы можем обрабатывать сообщения и отвечать на них при помощи добавления обработчика события onmessage подобным образом (worker.js):
Обработчик onmessage позволяет нам запустить некий код всякий раз, когда получен пакет с сообщением, доступным в атрибуте data события message. В примере выше мы просто перемножаем вместе две цифры, после чего используем postMessage() снова, чтобы отправить полученный результат назад в основной поток.
Возвращаясь в основной поток, мы используем onmessage снова, чтобы отреагировать на сообщение, отправленное нам назад из worker-а:
В примере выше мы берём данные из события сообщения и ставим их как textContent у результирующего абзаца, чтобы показать пользователю результат этой калькуляции.
Примечание: Обратите внимание, что onmessage() и postmessage() должны вызываться из экземпляра Worker в главном потоке, но не в потоке worker-а. Это связано с тем, что внутри потока worker-а, worker выступает в качестве глобального объекта.
Примечание: При передаче сообщения между основным потоком и потоком worker-а, оно копируется или "передаётся" (перемещается), не делится между потоками. Читайте Transferring data to and from workers: further details для более подробного объяснения.
Прекращение работы worker-а главного потока достигается методом terminate:
Поток worker-а немедленно уничтожается.
При ошибке во время выполнения worker-а, вызывается его обработчик событий onerror. Он принимает событие error, которое реализует интерфейс ErrorEvent.
Событие не всплывает и его можно отменить. Для отмены действия по умолчанию, worker может вызвать метод preventDefault() в обработчике события ошибки.
У события ошибки есть три поля, которые представляют интерес:
messageСообщение об ошибке в читаемом виде.
filenameИмя файла со скриптом, в котором ошибка произошла.
linenoНомер строки в файле, в котором произошла ошибка.
Worker-ы могут запускать другие worker-ы. Так называемые sub-worker'ы должны быть того же происхождения (same-origin), что и родительский документ. Кроме того, URI для subworker-ов рассчитываются относительно родительского worker'а, а не родительского документа. Это позволяет worker-ам проще следить за тем, где находятся их зависимости.
Worker потоки имеют доступ к глобальной функции, importScripts(), которая позволяет импортировать скрипты с того же домена в их область видимости. Функция принимает ноль и более URI параметров, как список ссылок на ресурсы для импорта; все нижеприведённые примеры верны:
Браузер загружает каждый указанный скрипт и исполняет его. Любые глобальные объекты, создаваемые каждым скриптом могут быть использованы в worker'е. Если скрипт не удалось загрузить, будет брошена ошибка NETWORK_ERROR, и последующий код не будет исполнен. Тем не менее код, исполненный ранее (включая отложенный при помощи window.setTimeout()) останется функционален. Объявления функций идущие после вызова метода importScripts() также будут доступны, т.к. объявления функций всегда обрабатываются перед остальным кодом.
Примечание: Скрипты могут быть загружены в произвольном порядке, но их исполнение будет в том порядке, в котором имена файлов были переданы в importScripts(). Функция выполняется синхронно; importScripts() не вернёт исполнение, пока все скрипты не будут загружены и исполнены.
Разделяемый worker доступен нескольким разным скриптам — даже если они находятся в разных окнах, фреймах или даже worker-ах. В этом разделе мы обсудим JavaScript, который можно найти в нашем базовом примере разделяемых worker-ов (запустить разделяемый worker): Он очень похож на базовый пример выделенных worker-ов, за исключением двух функций, которые доступны из разных скриптовых файлов: умножение двух чисел или возведение числа в степень. Оба скрипта используют один и тот же worker для необходимых вычислений.
Здесь мы сосредоточимся на разнице между выделенными и разделёнными worker-ами. Обратите внимание, что в данном примере есть две HTML страницы с JavaScript-кодом, которые используют один и тот же файл worker-а.
Примечание: Если разделяемый worker может быть доступен из нескольких контекстов просмотра, то все они должны иметь одно и то же происхождение (одни и те же протокол, хост и порт).
Примечание: В Firefox разделяемый worker не может быть использован совместно документами в приватном и неприватном окне (Firefox bug 1177621).
Запуск разделяемого worker-а очень похож на запуск выделенного worker-а, но используется другой конструктор (см. index.html и index2.html) — в каждом документе необходимо поднять worker, для этого следует написать такой код:
Большая разница заключается в том, что с разделяемым worker-ом необходимо взаимодействовать через объект port — явно открыв порт, с помощью которого скрипты могут взаимодействовать с worker-ом (в случае выделенного worker-а это происходит неявно).
Соединение с портом должно быть осуществлено либо неявно, используя обработчик событие onmessage, либо явно, вызвав метод start() перед тем, как отправлять любые сообщения. Вызов метода start() необходим только тогда, когда подписка на событие реализована через метод addEventListener().
Примечание: Когда используется метод start() чтобы открыть соединение с портом, его необходимо вызывать и в родительском потоке и в потоке worker-а, если необходима двухсторонняя коммуникация.
Теперь сообщения могут быть отправлены worker-у, как и прежде, но метод postMessage() должен вызываться из объекта port (ещё раз, вы можете увидеть схожие конструкции в multiply.js и square.js):
Теперь на стороне worker-а. Здесь код немного сложнее (worker.js):
Первый этап состоит из события onconnect. Оно срабатывает, когда произошло подключение (т.е. когда в родительском потоке отработало событие onmessage или когда в нем был вызван метод start()).
Мы используем атрибут события ports, чтобы получить порт и сохранить его в переменной.
Второй этап — это обработчик события message на сохранённом порту. Он нужен для подсчёта и вывода результата вычисления в основной поток. Установка обработчика message в потоке worker-а также открывает подключение к родительскому потоку, поэтому вызов на port.start() на самом деле не нужен (см. код обработчика onconnect).
Последний этап — возвращение в основной поток и обработка сообщения от worker‑а (ещё раз, вы можете увидеть схожие конструкции в multiply.js и square.js):
Когда сообщение приходит через порт от worker-а, мы проверяем тип результата вычислений и затем вставляем его в соответствующий абзац.
Интерфейс Worker создаёт настоящие потоки на уровне операционной системы, что может смутить опытных программистов и навести их на мысли о проблемах, связанных с конфликтом доступа к общим объектам.
На самом деле создать такие проблемы достаточно сложно, так как worker-ы жёстко контролируются. У них нет доступа к непотокобезопасным объектам DOM, а все данные между потоками передаются в качестве сериализованных объектов. Придётся очень постараться, чтобы вызывать проблемы потокобезопасности в вашем коде.
Передача данных между главной страницей и worker-ом происходит путём копирования, а не передачи по ссылке. Объекты сериализуются при передаче и затем десериализуются на другом конце. Страница и worker не используют совместно одни и те же экземпляры, для каждого создаётся свой. Большинство браузеров реализуют это структурированным клонированием (structured cloning).
Для иллюстрации этого мы создадим функцию emulateMessage(), которая будет имитировать поведение значения, которое клонируется, но не используется совместно при переходе от worker-а к главной странице или наоборот.
Значения, которые клонируются и совместно не используются, называются сообщениями. Как вы, возможно, знаете, сообщения могут быть отправлены в главную страницу и из неё, используя postMessage(), и data, содержа данные, передаваемые из worker-а.
example.html: (главная страница):
my_task.js (worker):
Алгоритм структурированного клонирования может принять JSON и некоторые вещи, которые JSON не может принять, например, циклические ссылки.
Если вам нужно передать сложные данные и вызвать множество различных функций как на главной странице, так и в worker-е, вы можете создать следующую систему.
В первую очередь мы создаём класс QueryableWorker, который принимает url worker-а, стандартный обработчик событий (defaultListener) и обработчик ошибок. Этот класс будет отслеживать всех обработчиков и поможет нам общаться с воркером.
Затем мы добавляем методы добавления/удаления обработчиков.
Здесь мы создадим у worker-а два простых события для примера: получение разницы двух чисел и создание оповещения через три секунды. Но сначала нам нужно реализовать метод sendQuery, который проверит есть ли вообще у worker-а обработчик, который мы собираемся вызвать.
Завершим QueryableWorker методом onmessage. Если worker имеет соответствующий метод, который мы запросили, он также должен вернуть соответствующий обработчик и аргументы, которые нам нужны. Останется лишь найти его в listeners:
Теперь к самому worker-у. Сначала следует определить эти два простых метода:
И onmessage:
Полный код примера:
example.html (основная страница):
my_task.js (код worker-а):
Можно переключать содержимое каждой главной страницы -> worker и worker -> сообщение главной страницы. И имена свойств "queryMethod", "queryMethodListeners", "queryMethodArguments" могут быть любыми пока они согласуются с QueryableWorker и worker.
Google Chrome 17+ and Firefox 18+ имеют дополнительную возможность передачи определённых типов объектов (передаваемые объекты реализующие Transferable интерфейс) к или из worker-а с высокой производительностью. Эти объекты передаются из одного контекста в другой без операций копирования, что приводит к значительному повышению производительности при отправке больших наборов данных. Думайте об этом как о передаче по ссылке в мире C/C++. Однако в отличии от передачи по ссылке, "версия" из вызывающего контекста больше недоступна после передачи. Владельцем становится новый контекст. Для примера, после передачи ArrayBuffer из главной страницы к worker-у, исходный ArrayBuffer очищается и более недоступен для использования. Его содержание (в буквальном смысле) переносится в рабочий контекст.
Примечание: Для дополнительной информации о передаваемых объектах, производительности и поддержки для этого метода, читайте Transferable Objects: Lightning Fast! на HTML5 Rocks.
Не существует утверждённого способа встроить код worker-а в рамках веб-страницы, как элемент <script> делает для обычных скриптов. Но элемент <script>, который не имеет атрибута src и атрибута type, которому не назначен выполняемый MIME type, можно считать блоком данных для использования JavaScript. Блок данных "Data blocks" — это более общее свойство HTML5, может содержать любые текстовые данные. Так, worker может быть встроен следующим образом:
Встраиваемый worker теперь внесён в новое custom свойство document.worker Также стоит отметить, что вы также можете преобразовать функцию в BLOB-объект, а затем сгенерировать URL объекта из этого BLOB-объекта. Например:
В этой секции представлено ещё несколько примеров как использовать worker-ы.
Worker-ы в основном полезны для того, чтобы позволить вашему коду выполнять ресурсоёмкие вычисления, не блокируя поток пользовательского интерфейса. В этом примере, worker используется для вычисления числа Фибоначчи.
Следующий код JavaScript хранится в файле "fibonacci.js", на который ссылается HTML в следующем разделе.
Worker устанавливает свойство onmessage для функции, которая будет получать сообщения, отправленные при вызове postMessage() рабочего объекта (обратите внимание, что это отличается от определения глобальной переменной с таким именем или определения функции с таким именем. var onmessage и function onmessage будет определять глобальные свойства с этими именами , но они не будут регистрировать функцию для получения сообщений, отправленных веб-страницей, которая создала worker). Это запускает рекурсию, порождая новые копии для обработки каждой итерации вычисления.
Веб-страница создаёт элемент div с ID result , который используется для отображения результата, а затем порождает worker. После порождения worker-а, обработчик onmessage настроен для отображения результатов путём установки содержимого элемента div, и обработчик onerror настроен на выброс сообщения об ошибке.
Наконец, сообщение отправляется worker-у, чтобы запустить его.
Вы можете найти пример этого в статье Использование worker-ов в расширениях.
Поскольку многоядерные компьютеры становятся все более распространёнными, часто бывает полезно разделить вычислительно сложные задачи между несколькими worker-ами, которые затем могут выполнить эти задачи на многопроцессорных ядрах.
В дополнение к выделенным и совместно используемым web worker-ам доступны другие типы worker-ов:
Внутри web worker-а вы можете использовать большинство стандартных функций JavaScript, включая:
Главное, что вы не можете сделать в Worker это напрямую повлиять на родительскую страницу. Это включает в себя манипулирование DOM и использование объектов этой страницы. Вы должны сделать это косвенно, отправив сообщение обратно основному сценарию через DedicatedWorkerGlobalScope.postMessage, а затем выполнив изменения оттуда.
Примечание: Для знакомства с полным списком функций, доступных для worker-ов, смотрите статью Функции и интерфейсы доступные worker-ам.
| HTML # workers |
This page was last modified on 27 окт. 2025 г. by MDN contributors.
Your blueprint for a better internet.
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998–2026 by individual mozilla.org contributors. Content available under a Creative Commons license.