Web Workers API служит для запуска скриптов в фоне. Если нам необходимо запустить в фоне какие-либо вычисления, да еще и в несколько потоков, то это подходящий инструмент для таких целей. Так как работа происходит в фоне, то при этих вычислениях рендеринг страницы не блокируется. Все происходит параллельно.
Доступность в браузерах по данным CanIUse
С создавшим его кодом каждый Web Worker общаеся через отправку событий.
Есть разные типы вокеров. Простейшим является выделенный воркер.
Создание выделенного воркера происходит просто:
1 2 3 4 5 6 7 8 9 10 |
if (window.Worker) { // Создание воркера var myWorker = new Worker("worker.js"); // Обработчик событий (получение данных) от воркера myWorker.onmessage = function(e) { console.log("Message received from worker", e.data); } // Отправка данных воркеру myWorker.postMessage([34, 10]); } |
Здесь проверяется доступность Web Wokres API и создается воркер из файла worker.js, а также добавляется обработка событий от воркера.
Код worker.js может содержать функцию onmessage
для приема данных от основного js кода:
1 2 3 4 5 6 7 8 |
onmessage = function(e) { // Получение данных от основного кода console.log("Message received from main script", e.data); var workerResult = e.data[0]*e.data[1]; console.log("Posting message back to main script"); // Отправка результата в основной код postMessage(workerResult); } |
Если первый код вставить в index.html между тегами script, а второй кусок кода поместить в wokrer.js, то запустив index.html мы получим в консоли браузера сообщения:
1 2 3 |
Message received from main script (2) [34, 10] Posting message back to main script Message received from worker 340 |
У MDN есть пример simple-web-worker. И demo. Там код лишь немного сложнее. Показаны 2 воркера.
Worker-ы могут запускать другие worker-ы. Главное чтобы политика одного источника (в браузере) соблюдалась.
Также из воркеров имеется доступ к функции importScripts("foo.js", "bar.js" ...);
для загрузки скриптов извне. Загрузка должна быть только с того же домена.
В воркере нельзя обращаться к DOM. Нет доступа к localStorage. Эти вещи можно делать только передав событие основному коду, который сделает все что нужно.
Разделяемый воркер доступен нескольким разным скриптам — даже если они находятся в разных окнах, фреймах или даже воркерах.
Создается такой воркер похожим образом:
1 |
var myWorker = new SharedWorker("worker.js"); |
Как видно, в данном случае используется конструктор SharedWorker.
С разделяемым worker-ом необходимо взаимодействовать через объект port
— явно открыв порт, с помощью которого скрипты могут взаимодействовать с worker-ом. Для выделенного worker-а это происходит тоже, но неявно (через onmessage
).
Соединение с портом должно быть осуществлено либо неявно, используя обработчик событие onmessage
, либо явно, вызвав метод start()
перед тем, как отправлять любые сообщения.
Например, в родительском потоке можно
1 2 3 4 |
// Открываем порт myWorker.port.start(); // Передаем данные myWorker.port.postMessage(["value1", "value2", "value3"]); |
При этом на стороне воркера:
1 2 3 4 5 6 7 8 |
// Необходим addEventListener() для события onconnect self.addEventListener("connect", function(e) { var port = e.ports[0]; // Получаем первый порт port.onmessage = function(e) { var workerResult = "Result: " + (e.data[0] + e.data[1] + e.data[2]); port.postMessage(workerResult); } }); |
В родительском потоке:
1 2 3 |
myWorker.port.onmessage = function(e) { console.log("Message received from worker", e.data[0]); } |
Таким образом, если раньше у нас был 1 обработчик данных в воркере, то теперь их может быть много с разным функционалом. Что может быть использовано для связи с различными скриптами.
Данные в воркеры и из них передаются копированием при помощи сериализации. По ссылке данные не передаются. Однако у Chrome 17+ и Firefox 18+ имеется возможность передать некоторые типы объектов по ссылке. Это объекты, которые реализуют интерфейс Transferable. Пока что это только ArrayBuffer и MessagePort.
Чтобы передать таким образом параметр необходимо воспользоваться вторым аргуметом метода postMessage
:
1 2 |
var ab = new ArrayBuffer(size); worker.postMessage({ data: ab }, [ab]); |
Имеются развноидности воркеров.