This page was translated from English by the community.Learn more and join the MDN Web Docs community.
Использование Web Workers
Web Worker-ы предоставляют простое средство для запуска скриптов в фоновом потоке. Поток Worker'а может выполнять задачи без вмешательства в пользовательский интерфейс. К тому же, они могут осуществлять ввод/вывод, используяXMLHttpRequest (хотя атрибутыresponseXML иchannel всегда будут равны null). Существующий Worker может отсылать сообщения JavaScript коду-создателю через обработчик событий, указанный этим кодом (и наоборот). Эта статья даёт детальную инструкцию по использованию Web Workers.
In this article
Web Workers API
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 OSData Store API. Для дополнительной информации смотритеFunctions and classes available to workers.
Данные передаются между worker-ами и главным потоком через систему сообщений — обе стороны передают свои сообщения, используя методpostMessage() и отвечают на сообщения при помощи обработчика событийonmessage (сообщение хранится в атрибуте data событияMessage). Данные при этом копируются, а не делятся.
Объекты Workers могут, в свою очередь, создавать новые объекты workers, и так до тех пор, пока всё работает в рамках текущей страницы. Плюс к этому, объекты workers могут использоватьXMLHttpRequest для сетевого ввода/вывода, но есть исключение - атрибутыresponseXML иchannel объектаXMLHttpRequest всегда возвращаютnull.
Выделенные Workers
Как уже упоминалось выше, выделенный Worker доступен только для скрипта, который его вызвал. В этом разделе речь пойдёт о JavaScript, который можно найти в нашемосновном примере выделенного Worker (запустить скрипт): этот пример позволяет ввести два числа для умножения. Эти числа отправляются в Worker, перемножаются, а результат возвращается на страницу и отображается.
Этот пример достаточно тривиален, но для ознакомления с базовыми концепциями worker-ов мы решили его упростить. Более продвинутые детали описаны далее в статье.
Определение поддержки Worker
Для большего контроля над ошибками и обратной совместимости, рекомендуется обернуть ваш код доступа к worker-у в следующий (main.js):
if (window.Worker) { ...}Создание выделенного worker
Создание нового worker-а — это легко. Всё что вам нужно это вызвать конструкторWorker(), указав URI скрипта для выполнения в потоке worker-а (main.js):
var myWorker = new Worker("worker.js");Передача сообщений в и из выделенного worker
Магия worker-ов происходит черезpostMessage() метод и обработчик событийonmessage. Когда вы хотите отправить сообщение в worker, вы доставляете сообщение к нему вот так (main.js):
first.onchange = function () { myWorker.postMessage([first.value, second.value]); console.log("Message posted to worker");};second.onchange = function () { myWorker.postMessage([first.value, second.value]); console.log("Message posted to worker");};В приведённом фрагменте кода мы имеем два<input> элемента, представленных переменнымиfirst иsecond; когда значение любой из переменных изменяется,myWorker.postMessage([first.value,second.value]) используется для отправки обоих значений, представленных в виде массива, в worker. Посредством аргументаmessage возможна передача практически любых данных в worker.
Внутри worker-a мы можем обрабатывать сообщения и отвечать на них при помощи добавления обработчика событияonmessage подобным образом (worker.js):
onmessage = function (e) { console.log("Message received from main script"); var workerResult = "Result: " + e.data[0] * e.data[1]; console.log("Posting message back to main script"); postMessage(workerResult);};Обработчикonmessage позволяет нам запустить некий код всякий раз, когда получен пакет с сообщением, доступным в атрибутеdata событияmessage. В примере выше мы просто перемножаем вместе две цифры, после чего используемpostMessage() снова, чтобы отправить полученный результат назад в основной поток.
Возвращаясь в основной поток, мы используемonmessage снова, чтобы отреагировать на сообщение, отправленное нам назад из worker-а:
myWorker.onmessage = function (e) { result.textContent = e.data; console.log("Message received from worker");};В примере выше мы берём данные из события сообщения и ставим их какtextContent у результирующего абзаца, чтобы показать пользователю результат этой калькуляции.
Примечание:Обратите внимание, чтоonmessage() иpostmessage() должны вызываться из экземпляра Worker в главном потоке, но не в потоке worker-а. Это связано с тем, что внутри потока worker-а, worker выступает в качестве глобального объекта.
Примечание:При передаче сообщения между основным потоком и потоком worker-а, оно копируется или "передаётся" (перемещается), не делится между потоками. ЧитайтеTransferring data to and from workers: further details для более подробного объяснения.
Завершение работы worker-а
Прекращение работы worker-а главного потока достигается методомterminate:
myWorker.terminate();Поток worker-а немедленно уничтожается.
Обработка ошибок
При ошибке во время выполнения worker-а, вызывается его обработчик событийonerror. Он принимает событиеerror, которое реализует интерфейсErrorEvent.
Событие не всплывает и его можно отменить. Для отмены действия по умолчанию, worker может вызвать методpreventDefault() в обработчике события ошибки.
У события ошибки есть три поля, которые представляют интерес:
Создание subworkers
Worker-ы могут запускать другие worker-ы. Так называемые sub-worker'ы должны быть того же происхождения (same-origin), что и родительский документ. Кроме того, URI для subworker-ов рассчитываются относительно родительского worker'а, а не родительского документа. Это позволяет worker-ам проще следить за тем, где находятся их зависимости.
Импорт скриптов и библиотек
Worker потоки имеют доступ к глобальной функции,importScripts(), которая позволяет импортировать скрипты с того же домена в их область видимости. Функция принимает ноль и более URI параметров, как список ссылок на ресурсы для импорта; все нижеприведённые примеры верны:
importScripts(); /* imports nothing */importScripts("foo.js"); /* imports just "foo.js" */importScripts("foo.js", "bar.js"); /* imports two scripts */Браузер загружает каждый указанный скрипт и исполняет его. Любые глобальные объекты, создаваемые каждым скриптом могут быть использованы в worker'е. Если скрипт не удалось загрузить, будет брошена ошибкаNETWORK_ERROR, и последующий код не будет исполнен. Тем не менее код, исполненный ранее (включая отложенный при помощиwindow.setTimeout()) останется функционален. Объявления функций идущиепосле вызова методаimportScripts() также будут доступны, т.к. объявления функций всегда обрабатываются перед остальным кодом.
Примечание:Скрипты могут быть загружены в произвольном порядке, но их исполнение будет в том порядке, в котором имена файлов были переданы вimportScripts(). Функция выполняется синхронно;importScripts() не вернёт исполнение, пока все скрипты не будут загружены и исполнены.
Разделяемые worker-ы (Shared workers)
Разделяемый worker доступен нескольким разным скриптам — даже если они находятся в разных окнах, фреймах или даже worker-ах. В этом разделе мы обсудим JavaScript, который можно найти в нашембазовом примере разделяемых worker-ов (запустить разделяемый worker): Он очень похож на базовый пример выделенных worker-ов, за исключением двух функций, которые доступны из разных скриптовых файлов:умножение двух чисел иливозведение числа в степень. Оба скрипта используют один и тот же worker для необходимых вычислений.
Здесь мы сосредоточимся на разнице между выделенными и разделёнными worker-ами. Обратите внимание, что в данном примере есть две HTML страницы с JavaScript-кодом, которые используют один и тот же файл worker-а.
Примечание:Если разделяемый worker может быть доступен из нескольких контекстов просмотра, то все они должны иметь одно и то же происхождение (одни и те же протокол, хост и порт).
Примечание:В Firefox разделяемый worker не может быть использован совместно документами в приватном и неприватном окне (Firefox bug 1177621).
Создание разделяемого worker-а
Запуск разделяемого worker-а очень похож на запуск выделенного worker-а, но используется другой конструктор (см.index.html иindex2.html) — в каждом документе необходимо поднять worker, для этого следует написать такой код:
var myWorker = new SharedWorker("worker.js");Большая разница заключается в том, что с разделяемым worker-ом необходимо взаимодействовать через объектport — явно открыв порт, с помощью которого скрипты могут взаимодействовать с worker-ом (в случае выделенного worker-а это происходит неявно).
Соединение с портом должно быть осуществлено либо неявно, используя обработчик событиеonmessage, либо явно, вызвав методstart() перед тем, как отправлять любые сообщения. Вызов метода start() необходим только тогда, когда подписка на событие реализована через методaddEventListener().
Примечание:Когда используется методstart() чтобы открыть соединение с портом, его необходимо вызывать и в родительском потоке и в потоке worker-а, если необходима двухсторонняя коммуникация.
myWorker.port.start(); // в родительском потокеport.start(); // в потоке worker-а, где переменная port является ссылкой на портПередача сообщений в/из разделяемого worker-а
Теперь сообщения могут быть отправлены worker-у, как и прежде, но методpostMessage() должен вызываться из объектаport (ещё раз, вы можете увидеть схожие конструкции вmultiply.js иsquare.js):
squareNumber.onchange = function () { myWorker.port.postMessage([squareNumber.value, squareNumber.value]); console.log("Message posted to worker");};Теперь на стороне worker-а. Здесь код немного сложнее (worker.js):
self.addEventListener("connect", function (e) { // требуется addEventListener() var port = e.ports[0]; port.onmessage = function (e) { var workerResult = "Result: " + e.data[0] * e.data[1]; port.postMessage(workerResult); }; port.start(); // вызов необязательный, т.к. используется обработчик событий onmessage});Первый этап состоит из событияonconnect. Оно срабатывает, когда произошло подключение (т.е. когда в родительском потоке отработало событиеonmessage или когда в нем был вызван методstart()).
Мы используем атрибут событияports, чтобы получить порт и сохранить его в переменной.
Второй этап — это обработчик событияmessage на сохранённом порту. Он нужен для подсчёта и вывода результата вычисления в основной поток. Установка обработчикаmessage в потоке worker-а также открывает подключение к родительскому потоку, поэтому вызов наport.start() на самом деле не нужен (см. код обработчикаonconnect).
Последний этап — возвращение в основной поток и обработка сообщения от worker‑а (ещё раз, вы можете увидеть схожие конструкции вmultiply.js иsquare.js):
myWorker.port.onmessage = function (e) { result2.textContent = e.data[0]; console.log("Message received from worker");};Когда сообщение приходит через порт от worker-а, мы проверяем тип результата вычислений и затем вставляем его в соответствующий абзац.
О потоковой безопасности
ИнтерфейсWorker создаёт настоящие потоки на уровне операционной системы, что может смутить опытных программистов и навести их на мысли о проблемах, связанных с конфликтом доступа к общим объектам.
На самом деле создать такие проблемы достаточно сложно, так как worker-ы жёстко контролируются. У них нет доступа к непотокобезопасным объектам DOM, а все данные между потоками передаются в качестве сериализованных объектов. Придётся очень постараться, чтобы вызывать проблемы потокобезопасности в вашем коде.
Передача данных в и из worker-ов: другие детали
Передача данных между главной страницей и worker-ом происходит путём копирования, а не передачи по ссылке. Объекты сериализуются при передаче и затем десериализуются на другом конце. Страница и worker не используют совместно одни и те же экземпляры, для каждого создаётся свой. Большинство браузеров реализуют это структурированным клонированием (structured cloning).
Для иллюстрации этого мы создадим функциюemulateMessage(), которая будет имитировать поведение значения, которое клонируется, но не используется совместно при переходе от worker-а к главной странице или наоборот.
function emulateMessage(vVal) { return eval("(" + JSON.stringify(vVal) + ")");}// Tests// test #1var example1 = new Number(3);console.log(typeof example1); // objectconsole.log(typeof emulateMessage(example1)); // number// test #2var example2 = true;console.log(typeof example2); // booleanconsole.log(typeof emulateMessage(example2)); // boolean// test #3var example3 = new String("Hello World");console.log(typeof example3); // objectconsole.log(typeof emulateMessage(example3)); // string// test #4var example4 = { name: "John Smith", age: 43,};console.log(typeof example4); // objectconsole.log(typeof emulateMessage(example4)); // object// test #5function Animal(sType, nAge) { this.type = sType; this.age = nAge;}var example5 = new Animal("Cat", 3);alert(example5.constructor); // Animalalert(emulateMessage(example5).constructor); // ObjectЗначения, которые клонируются и совместно не используются, называются сообщениями. Как вы, возможно, знаете, сообщения могут быть отправлены в главную страницу и из неё, используяpostMessage(), иdata, содержа данные, передаваемые из worker-а.
example.html: (главная страница):
var myWorker = new Worker("my_task.js");myWorker.onmessage = function (oEvent) { console.log("Worker said : " + oEvent.data);};myWorker.postMessage("ali");my_task.js (worker):
postMessage("I'm working before postMessage('ali').");onmessage = function (oEvent) { postMessage("Hi " + oEvent.data);};Алгоритм структурированного клонирования может принять JSON и некоторые вещи, которые JSON не может принять, например, циклические ссылки.
Примеры передачи данных
Пример #1: Расширенная передача JSON данных и создание системы коммутации
Если вам нужно передать сложные данные и вызвать множество различных функций как на главной странице, так и в worker-е, вы можете создать следующую систему.
В первую очередь мы создаём класс QueryableWorker, который принимает url worker-а, стандартный обработчик событий (defaultListener) и обработчик ошибок. Этот класс будет отслеживать всех обработчиков и поможет нам общаться с воркером.
function QueryableWorker(url, defaultListener, onError) { var instance = this, worker = new Worker(url), listeners = {}; this.defaultListener = defaultListener || function () {}; if (onError) { worker.onerror = onError; } this.postMessage = function (message) { worker.postMessage(message); }; this.terminate = function () { worker.terminate(); };}Затем мы добавляем методы добавления/удаления обработчиков.
this.addListeners = function (name, listener) { listeners[name] = listener;};this.removeListeners = function (name) { delete listeners[name];};Здесь мы создадим у worker-а два простых события для примера: получение разницы двух чисел и создание оповещения через три секунды. Но сначала нам нужно реализовать метод sendQuery, который проверит есть ли вообще у worker-а обработчик, который мы собираемся вызвать.
/* Эта функция принимает по крайней мере один аргумент: имя метода, который мы хотим вызвать. Далее мы можем передать методу необходимые ему аргументы. */this.sendQuery = function () { if (arguments.length < 1) { throw new TypeError( "QueryableWorker.sendQuery takes at least one argument", ); return; } worker.postMessage({ queryMethod: arguments[0], queryArguments: Array.prototype.slice.call(arguments, 1), });};Завершим QueryableWorker методомonmessage. Если worker имеет соответствующий метод, который мы запросили, он также должен вернуть соответствующий обработчик и аргументы, которые нам нужны. Останется лишь найти его вlisteners:
worker.onmessage = function (event) { if ( event.data instanceof Object && event.data.hasOwnProperty("queryMethodListener") && event.data.hasOwnProperty("queryMethodArguments") ) { listeners[event.data.queryMethodListener].apply( instance, event.data.queryMethodArguments, ); } else { this.defaultListener.call(instance, event.data); }};Теперь к самому worker-у. Сначала следует определить эти два простых метода:
var queryableFunctions = { getDifference: function (a, b) { reply("printStuff", a - b); }, waitSomeTime: function () { setTimeout(function () { reply("doAlert", 3, "seconds"); }, 3000); },};function reply() { if (arguments.length < 1) { throw new TypeError("reply - takes at least one argument"); return; } postMessage({ queryMethodListener: arguments[0], queryMethodArguments: Array.prototype.slice.call(arguments, 1), });}/* This method is called when main page calls QueryWorker's postMessage method directly*/function defaultReply(message) { // do something}Иonmessage:
onmessage = function (event) { if ( event.data instanceof Object && event.data.hasOwnProperty("queryMethod") && event.data.hasOwnProperty("queryMethodArguments") ) { queryableFunctions[event.data.queryMethod].apply( self, event.data.queryMethodArguments, ); } else { defaultReply(event.data); }};Полный код примера:
example.html (основная страница):
<!doctype html><html> <head> <meta charset="UTF-8" /> <title>MDN Example - Queryable worker</title> <script type="text/javascript"> /* QueryableWorker instances methods: * sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function * postMessage(string or JSON Data): see Worker.prototype.postMessage() * terminate(): terminates the Worker * addListener(name, function): adds a listener * removeListener(name): removes a listener QueryableWorker instances properties: * defaultListener: the default listener executed only when the Worker calls the postMessage() function directly */ function QueryableWorker(url, defaultListener, onError) { var instance = this, worker = new Worker(url), listeners = {}; this.defaultListener = defaultListener || function () {}; if (onError) { worker.onerror = onError; } this.postMessage = function (message) { worker.postMessage(message); }; this.terminate = function () { worker.terminate(); }; this.addListener = function (name, listener) { listeners[name] = listener; }; this.removeListener = function (name) { delete listeners[name]; }; /* This functions takes at least one argument, the method name we want to query. Then we can pass in the arguments that the method needs. */ this.sendQuery = function () { if (arguments.length < 1) { throw new TypeError( "QueryableWorker.sendQuery takes at least one argument", ); return; } worker.postMessage({ queryMethod: arguments[0], queryMethodArguments: Array.prototype.slice.call(arguments, 1), }); }; worker.onmessage = function (event) { if ( event.data instanceof Object && event.data.hasOwnProperty("queryMethodListener") && event.data.hasOwnProperty("queryMethodArguments") ) { listeners[event.data.queryMethodListener].apply( instance, event.data.queryMethodArguments, ); } else { this.defaultListener.call(instance, event.data); } }; } // your custom "queryable" worker var myTask = new QueryableWorker("my_task.js"); // your custom "listeners" myTask.addListener("printStuff", function (result) { document .getElementById("firstLink") .parentNode.appendChild( document.createTextNode("The difference is " + result + "!"), ); }); myTask.addListener("doAlert", function (time, unit) { alert("Worker waited for " + time + " " + unit + " :-)"); }); </script> </head> <body> <ul> <li> <a href="#" >What is the difference between 5 and 3?</a > </li> <li> <a href="#" >Wait 3 seconds</a > </li> <li> <a href="#">terminate() the Worker</a> </li> </ul> </body></html>my_task.js (код worker-а):
var queryableFunctions = { // пример #1: получить разницу между двумя числами getDifference: function (nMinuend, nSubtrahend) { reply("printStuff", nMinuend - nSubtrahend); }, // пример #2: подождать три секунды waitSomeTime: function () { setTimeout(function () { reply("doAlert", 3, "seconds"); }, 3000); },};// системные функцииfunction defaultReply(message) { // your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly // do something}function reply() { if (arguments.length < 1) { throw new TypeError("reply - not enough arguments"); return; } postMessage({ queryMethodListener: arguments[0], queryMethodArguments: Array.prototype.slice.call(arguments, 1), });}onmessage = function (oEvent) { if ( oEvent.data instanceof Object && oEvent.data.hasOwnProperty("queryMethod") && oEvent.data.hasOwnProperty("queryMethodArguments") ) { queryableFunctions[oEvent.data.queryMethod].apply( self, oEvent.data.queryMethodArguments, ); } else { defaultReply(oEvent.data); }};Можно переключать содержимое каждой главной страницы -> worker и worker -> сообщение главной страницы. И имена свойств "queryMethod", "queryMethodListeners", "queryMethodArguments" могут быть любыми пока они согласуются сQueryableWorker иworker.
Передача данных с помощью передачи владения (передаваемые объекты)
Google Chrome 17+ and Firefox 18+ имеют дополнительную возможность передачи определённых типов объектов (передаваемые объекты реализующиеTransferable интерфейс) к или из worker-а с высокой производительностью. Эти объекты передаются из одного контекста в другой без операций копирования, что приводит к значительному повышению производительности при отправке больших наборов данных. Думайте об этом как о передаче по ссылке в мире C/C++. Однако в отличии от передачи по ссылке, "версия" из вызывающего контекста больше недоступна после передачи. Владельцем становится новый контекст. Для примера, после передачиArrayBuffer из главной страницы к worker-у, исходныйArrayBuffer очищается и более недоступен для использования. Его содержание (в буквальном смысле) переносится в рабочий контекст.
// Create a 32MB "file" and fill it.var uInt8Array = new Uint8Array(1024 * 1024 * 32); // 32MBfor (var i = 0; i < uInt8Array.length; ++i) { uInt8Array[i] = i;}worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);Примечание:Для дополнительной информации о передаваемых объектах, производительности и поддержки для этого метода, читайтеTransferable Objects: Lightning Fast! на HTML5 Rocks.
Встроенные worker-ы
Не существует утверждённого способа встроить код worker-а в рамках веб-страницы, как элемент<script> делает для обычных скриптов. Но элемент<script>, который не имеет атрибутаsrc и атрибутаtype, которому не назначен выполняемый MIME type, можно считать блоком данных для использования JavaScript. Блок данных "Data blocks" — это более общее свойство HTML5, может содержать любые текстовые данные. Так, worker может быть встроен следующим образом:
<!doctype html><html> <head> <meta charset="UTF-8" /> <title>MDN Example - Embedded worker</title> <script type="text/js-worker"> // Этот script НЕ БУДЕТ анализироваться JS движками, потому что его MIME-тип text/js-worker. var myVar = 'Hello World!'; // Остальная часть кода вашего воркера идёт сюда. </script> <script type="text/javascript"> // Этот script БУДЕТ проанализирован JS движкам, потому что его MIME-тип text/javascript. function pageLog(sMsg) { // Use a fragment: browser will only render/reflow once. var oFragm = document.createDocumentFragment(); oFragm.appendChild(document.createTextNode(sMsg)); oFragm.appendChild(document.createElement("br")); document.querySelector("#logDisplay").appendChild(oFragm); } </script> <script type="text/js-worker"> // Этот script НЕ БУДЕТ анализироваться JS движками, потому что его MIME-тип text/js-worker. onmessage = function(oEvent) { postMessage(myVar); }; // Остальная часть кода вашего воркера идёт сюда. </script> <script type="text/javascript"> // Этот script БУДЕТ проанализирован JS движкам, потому что его MIME-тип text/javascript. // В прошлом...: // blob builder существовал // ... но теперь мы используем Blob...: var blob = new Blob( Array.prototype.map.call( document.querySelectorAll("script[type='text\/js-worker']"), function (oScript) { return oScript.textContent; }, ), { type: "text/javascript" }, ); // Создание нового свойства document.worker, содержащего все наши "text/js-worker" скрипты. document.worker = new Worker(window.URL.createObjectURL(blob)); document.worker.onmessage = function (oEvent) { pageLog("Received: " + oEvent.data); }; // Запуск воркера. window.onload = function () { document.worker.postMessage(""); }; </script> </head> <body> <div></div> </body></html>Встраиваемый worker теперь внесён в новое custom свойствоdocument.worker Также стоит отметить, что вы также можете преобразовать функцию в BLOB-объект, а затем сгенерировать URL объекта из этого BLOB-объекта. Например:
function fn2workerURL(fn) { var blob = new Blob(["(" + fn.toString() + ")()"], { type: "application/javascript", }); return URL.createObjectURL(blob);}Другие примеры
В этой секции представлено ещё несколько примеров как использовать worker-ы.
Выполнение вычислений в фоне
Worker-ы в основном полезны для того, чтобы позволить вашему коду выполнять ресурсоёмкие вычисления, не блокируя поток пользовательского интерфейса. В этом примере, worker используется для вычисления числа Фибоначчи.
Код JavaScript
Следующий код JavaScript хранится в файле "fibonacci.js", на который ссылается HTML в следующем разделе.
var results = [];function resultReceiver(event) { results.push(parseInt(event.data)); if (results.length == 2) { postMessage(results[0] + results[1]); }}function errorReceiver(event) { throw event.data;}onmessage = function (event) { var n = parseInt(event.data); if (n == 0 || n == 1) { postMessage(n); return; } for (var i = 1; i <= 2; i++) { var worker = new Worker("fibonacci.js"); worker.onmessage = resultReceiver; worker.onerror = errorReceiver; worker.postMessage(n - i); }};Worker устанавливает свойствоonmessage для функции, которая будет получать сообщения, отправленные при вызовеpostMessage() рабочего объекта (обратите внимание, что это отличается от определения глобальнойпеременной с таким именем или определенияфункции с таким именем.var onmessage иfunction onmessage будет определять глобальные свойства с этими именами , но они не будут регистрировать функцию для получения сообщений, отправленных веб-страницей, которая создала worker). Это запускает рекурсию, порождая новые копии для обработки каждой итерации вычисления.
HTML код
<!doctype html><html> <head> <meta charset="UTF-8" /> <title>Test threads fibonacci</title> </head> <body> <div></div> <script language="javascript"> var worker = new Worker("fibonacci.js"); worker.onmessage = function (event) { document.getElementById("result").textContent = event.data; dump("Got: " + event.data + "\n"); }; worker.onerror = function (error) { dump("Worker error: " + error.message + "\n"); throw error; }; worker.postMessage("5"); </script> </body></html>Веб-страница создаёт элементdiv с IDresult , который используется для отображения результата, а затем порождает worker. После порождения worker-а, обработчикonmessage настроен для отображения результатов путём установки содержимого элементаdiv, и обработчикonerror настроен навыброс сообщения об ошибке.
Наконец, сообщение отправляется worker-у, чтобы запустить его.
Выполнение веб I/O в фоне
Вы можете найти пример этого в статьеИспользование worker-ов в расширениях.
Разделение задач между множественными worker-ами
Поскольку многоядерные компьютеры становятся все более распространёнными, часто бывает полезно разделить вычислительно сложные задачи между несколькими worker-ами, которые затем могут выполнить эти задачи на многопроцессорных ядрах.
Другие типы worker-ов
В дополнение к выделенным и совместно используемым web worker-ам доступны другие типы worker-ов:
- ServiceWorkers, по сути, действуют как прокси-серверы, которые размещаются между веб-приложениями, браузером и сетью (при наличии). Они предназначены (помимо прочего) для создания эффективного автономного взаимодействия, перехвата сетевых запросов и принятия соответствующих действий в зависимости от того, доступна ли сеть, и обновлены ли ресурсы на сервере. Они также разрешают доступ push-уведомлениям и API фоновой синхронизации.
- Chrome Workers это worker типа Firefox-only, который вы можете использовать, если вы разрабатываете дополнения и хотите использовать worker-ы в расширениях и иметь доступ кjs-ctypes в вашем worker-е. Смотрите
ChromeWorkerдля более подробной информации. - Audio Workers предоставляют возможность прямой обработки звука по сценарию в контексте web worker-а.
Функции и интерфейсы доступные в worker-ах
Внутри web worker-а вы можете использовать большинство стандартных функций JavaScript, включая:
NavigatorXMLHttpRequestArray,Date,Math, иStringWindow.requestAnimationFrame,WindowTimers.setTimeout, иWindowTimers.setInterval
Главное, что вы не можете сделать в Worker это напрямую повлиять на родительскую страницу. Это включает в себя манипулирование DOM и использование объектов этой страницы. Вы должны сделать это косвенно, отправив сообщение обратно основному сценарию черезDedicatedWorkerGlobalScope.postMessage, а затем выполнив изменения оттуда.
Примечание:Для знакомства с полным списком функций, доступных для worker-ов, смотрите статьюФункции и интерфейсы доступные worker-ам.
Спецификации
| Specification |
|---|
| HTML> # workers> |
Смотрите также
- Интерфейс
Worker - Интерфейс
SharedWorker - Доступные для воркеров функции
- Интерфейс
OffscreenCanvas