Перейти к содержимому

Воркер что это в программировании

  • автор:

Worker

Интерфейс Worker из Web Workers API является представителем фоновой задачи, которая легко может быть создана и может передавать сообщения обратно создателю. Создание worker — это вызов конструктора Worker() и задание скрипта, выполняемого в потоке worker.

Workers могут, в свою очередь, порождать новые workers, если эти workers расположены в одном каталоге с родительской страницей (Примечание: вложенные workers в настоящее время не поддерживаются в Blink). Кроме того, workers могут использовать XMLHttpRequest для network I/O, при условии, что атрибуты responseXML и channel XMLHttpRequest всегда возвращают null .

В Firefox, если вы хотите использовать workers в расширениях и хотели бы иметь доступ к js-ctypes, вам нужно использовать объект ChromeWorker .

Свойства

Обработчики событий

EventListener вызываемый каждый раз, когда из воркера приходит ErrorEvent (en-US) с типом error . Это событие унаследовано от AbstractWorker (en-US).

EventListener вызываемый каждый раз, когда из воркера приходит MessageEvent (en-US) с типом message — то есть когда сообщение было отправлено воркером в родительский документ с помощью DedicatedWorkerGlobalScope.postMessage (en-US). Сообщение передаётся в свойстве объекта события data (en-US).

Конструкторы

Создаёт dedicated web worker, выполняющий скрипт, расположенный по указанному URL. Воркеры также могут быть созданы с использованием Blobs.

Методы

Наследует методы своего родителя, EventTarget , а также реализует методы AbstractWorker (en-US).

Отправляет сообщение — которое может являться любым JavaScript объектом — во внутреннюю область видимости воркера.

Немедленно завершает выполнение воркера. Не даёт воркеру возможности завершить выполняемые операции; просто сразу останавливается. Экземпляры ServiceWorker не поддерживают этот метод.

Пример

Представленный фрагмент кода показывает создание объекта Worker с помощью конструктора Worker() , а также его использование:

var myWorker = new Worker("worker.js"); first.onchange = function ()  myWorker.postMessage([first.value, second.value]); console.log("Message posted to worker"); >; 

Спецификации

Specification
HTML Standard
# dedicated-workers-and-the-worker-interface

Совместимость с браузерами

BCD tables only load in the browser

See also

  • Using web workers
  • Functions available to workers
  • Другие типы воркеров: SharedWorker и ServiceWorker (en-US).
  • Non-standard, Gecko-specific workers: ChromeWorker , used by extensions.

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 3 авг. 2023 г. by MDN contributors.

Your blueprint for a better internet.

MDN

Support

  • Product help
  • Report an issue

Our communities

Developers

  • Web Technologies
  • Learn Web Development
  • MDN Plus
  • Hacks Blog
  • Website Privacy Notice
  • Cookies
  • Legal
  • Community Participation Guidelines

Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2024 by individual mozilla.org contributors. Content available under a Creative Commons license.

Повышение производительности с помощью Web Workers

JavaScript — это однопоточный язык. Это означает, что движки JavaScript (или компиляторы) могут выполнять только одну инструкцию за раз. JavaScript не может быть многозадачным или выполнять код параллельно, в отличие от других языков программирования. Из этого вы можете сделать вывод, что JavaScript может выполнять только один скрипт за раз. Это может быть проблемой.

Очевидно, что если мы хотим выполнять несколько задач одновременно или выполнять более одной инструкции в данный момент, мы должны дождаться полного завершения текущей инструкции. Это называется блокировкой. И она становится серьезной проблемой, когда текущая инструкция требует огромного количества времени. Что, если нам придется ждать десять, двадцать или сорок секунд, пока текущая инструкция (например, функция или запрос к серверу) не завершит работу?

Это сделало бы остальную часть нашей страницы неотвечающей. Любая другая функциональность на нашем сайте, требующая JavaScript, не будет работать, потому что движок/компилятор JavaScript будет занят текущей инструкцией.

Для борьбы с блокировками у нас есть несколько инструментов. Одним из таких инструментов являются веб-воркеры. Что такое веб-воркеры?

Web Worker — это инструмент, предоставляемый браузерами, который позволяет разработчикам запускать дополнительный скрипт в фоновом режиме. Вы можете представить это как преобразование JavaScript из однопоточного языка в многопоточный.

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

Туториал

В этом туториале я собираюсь показать вам два сценария: один сценарий, в котором не используются веб-воркеры, и другой сценарий, в котором они используются. Если вы все еще не до конца понимаете назначение веб-воркеров, надеюсь, это руководство изменит это.

Для начала нам понадобятся два скрипта: основной скрипт и скрипт воркера. Я собираюсь присвоить имя index.js основному скрипту и worker.js — скрипту веб-воркера. Код скрипта index.js выглядит так:

Web Worker API

JavaScript представляет язык, который выполняется как однопоточный, а это означает, что несколько скриптов не могут выполняться одновременно. Скрипты интерпретируются и выполняются один за другим, строка за строкой.

Кроме того, запуск событий и их обработчиков не происходят параллельно: код, запускающий событие, приостанавливается до тех пор, пока не будут выполнены соответствующие обработчики событий.

То же самое относится и к коллбекам — функциям обратного вызова. Например, при отправке на сервер Ajax-запроса скрипт, который отправляет запрос, продолжает выполняться до тех пор, пока сервер не подготовит ответ и не отправит его клиенту. Когда коллбек получит ответ сервера, окружающий код приостанавливается и возобновляет свою работу, лишь когда коллбек завершит обработку ответа сервера.

Web Worker API снимает это ограничение, позволяя обрабатывать задачи параллельно в фоновом режиме. Веб-воркеры выполняются в отдельных потоках. Благодаря веб-воркерам становится возможным выполнять в фоновом режиме параллельно с основным потоком различные ресурсоемкие сценарии, которые в противном случае отрицательно повлияли бы на производительность веб-приложения. Поток веб-воркера может выполнять задачи, не вмешиваясь в пользовательский интерфейс.

Для создания веб-воркера применяется функция-конструктор Worker :

const worker = new Worker("worker.js");

Задачи, которые выполняет веб-воркеров, оформляются в виде отдельных файлов, и в функцию-конструктора в качестве параметра передается путь к скрипту, который будет выполняться веб-воркером.

Создаваемый функций Worker() веб-воркер еще называют выделенным веб-воркером (dedicated web worker).

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

Рассмотрим простейший пример. Определим для проекта на жестком диске папку, в которой создадим три файла:

  • index.html : главная страница приложения
  • worker.js : файл задачи веб-воркера
  • server.js : файл приложения сервера Node.js

Определение веб-страницы и создание веб-воркера

На странице index.html определим следующий код:

    METANIT.COM      

Фактически здесь только создается объект веб-воркера, который будет выполнять код из файла «worker.js».

Определение кода веб-воркера

В файле worker.js определим для демонстрации простейший код:

let result = 1; const intervalID = setInterval(work, 1000); function work() < result = result * 2; console.log("result b">server.js определим код локального веб-сервера Node.js. Определим в нем следующий код:

const http = require("http"); const fs = require("fs"); http.createServer((request, response)=>< // получаем путь после слеша, слеш - первый символ в пути let filePath = request.url.substring(1); // если пустой путь, отправляем главную страницу index.html if(!filePath) filePath = "index.html"; // в качестве типа ответа устанавливаем html response.setHeader("Content-Type", "text/html; charset=utf-8;"); fs.readFile(filePath, (error, data)=>< if(error)< // если ошибка response.statusCode = 404; response.end("

Resourse not found!

"); > else < response.end(data); >>); >).listen(3000, ()=>console.log("Сервер запущен по адресу http://localhost:3000"));

Вкратце пробежимся по коду. Сначала подключаются пакеты с функциональностью, которую мы собираемся использовать:

const http = require("http"); // для обработки входящих запросов const fs = require("fs"); // для чтения файлов с жесткого диска

Для создания сервера применяется функция http.createServer() . В эту функцию передается функция-обработчик, которая вызывается каждый раз, когда к серверу приходит запрос. Эта функция имеет два параметра: request (содержит данные запроса) и response (управляет отправкой ответа).

В функции-обработчике с помощью свойства request.url мы можем получить путь к ресурсу, к которому пришел запрос. Нам надо обрабатывать запросы к страницам "index.html" и "home.html" (а в перспективе к любым другим страницам html). Путь всегда начинается со слеша "/". Например, запрос к странице "home.html" будет представлять путь "/home.html". Соответственно, чтобы получить из запрошенного пути путь к файлам на жестком диске, нам надо убрать начальный слеш:

let filePath = request.url.substring(1);

Однако если запрос обращен к корню сайта, то путь состоит только из одного слеша - "/". Соответственно, если мы удалим этот слеш, то получим пустую строку. Поэтому если запрос идет к корню веб-приложения, то будем считать что запрос идет к главной странице - index.html:

if(!filePath) filePath = "index.html";

И поскольку в нашем случае ответ сервера будет представлять код html, то с помощью метода setHeader() устанавливаем для заголовка "Content-Type" значение "text/html":

response.setHeader("Content-Type", "text/html; charset=utf-8;");

То есть ответ сервера будет представлять html.

Далее с помощью функции fs.readFile считываем файл, к которому идет запрос. Первый параметр функции - адрес файла (в данном случае предполагается, что файл находится в одной папке с файлом сервера server.js). Второй параметр - функция, которая вызывается после считывания файла и получет его содержимое через свой второй параметр data. Вполне возможно, что запрошенного файла не окажется, и в этом случае отправляем ошибку 404:

fs.readFile(filePath, (error, data)=>< if(error)< // если ошибка response.statusCode = 404; response.end("

Resourse not found!

"); >

Если ошибки нет, файл найден и успещно считан, то отправляем параметр data, который содержит данные файла:

else

В конце с помощью функции listen() запускаем веб-сервер на 3000 порту. То есть сервер будет запускаться по адресу http://localhost:3000/

Запуск и тестирование приложения

Теперь в консоли перейдем к папке сервера с помощью команды cd и запустим сервер с помощью команды node server.js

C:\app>node server.js Сервер запущен по адресу http://localhost:3000

После запуска сервера мы можем перейти в браузере по адресу http://localhost:3000, нам отобразится страница, в javascript-коде которой будет создан веб-воркер. Этот веб-воркер выполнит задачу, определенную в файле worker.js , а на консоли мы увидим результат этой работы:

Тип Worker в Web Worker API и параллельное выполнение фоновых задач в javascript

Ограничения веб-воркера

В примере выше в коде веб-воркера использовался таймер, создаваемый функцией setInterval() . Однако не всю функциональность стандартного браузерного JavaScript мы можем использовать в задачах веб-воркера. Так, веб-воркеры не имеют доступа к DOM и к объекту window . Тем не менее часть возможностей объекта window (свойств и методов) доступны для веб-воркера (как в случае с функцией setInterval() ). В частности, доступны следующие функции:

  • atob()
  • btoa()
  • clearInterval()
  • clearTimeout()
  • queueMicrotask()
  • setInterval()
  • setTimeout()
  • structuredClone()
  • requestAnimationFrame() (только для выделенных веб-воркеров)
  • cancelAnimationFrame() (только для выделенных веб-воркеров)

Также для веб-воркеров доступны следующие свойства объекта window :

  • console
  • location
  • navigator
  • indexDB

Кроме того, веб-воркеры могут использовать следующие API:

  • Barcode Detection API
  • Broadcast Channel API
  • Cache API
  • Channel Messaging API
  • Console API
  • Web Crypto API (например, Crypto)
  • CSS Font Loading API
  • CustomEvent
  • Encoding API (например, TextEncoder, TextDecoder)
  • Fetch API
  • FileReader
  • FormData
  • ImageBitmap
  • ImageData
  • IndexedDB
  • Media Source Extensions API
  • Network Information API
  • Notifications API
  • OffscreenCanvas (и API для работы с контекстом элемента canvas)
  • Performance API
  • Server-sent events
  • ServiceWorkerRegistration
  • URL API
  • WebCodecs_API
  • WebSocket
  • XMLHttpRequest

Получение веб-воркера и self

С помощью слова self в скрипте веб-воркера (worker.js) мы можем обращаться к объекту веб-воркера:

console.log(self); // получим данные о веб-воркере let result = 1; const intervalID = setInterval(work, 1000); function work() < result = result * 2; console.log("result b">terminate(). Например, изменим код веб-страницы index.html следующим образом:

    METANIT.COM      

Здесь на веб-странице определена кнопка, по нажатию на которую происходит остановка веб-воркера.

Worker-ы и shared worker-ы

Во всех популярных языках есть потоки (threads). В браузерном javascript для параллельной обработки используются worker-ы.
Под катом рассказ о том, как ими пользоваться, какие ограничения есть в воркерах и об особенностях взаимодействия с ними в разных браузерах.

Что такое worker

  • navigator
  • location
  • applicationCache
  • XHR, websocket
  • importScripts для синхронной загрузки скриптов

Создание worker-а

Worker создаётся из отдельного скрипта:

var worker = new Worker(scriptUrl); var sharedWorker = new SharedWorker(scriptUrl); 

Shared worker идентифицируется по URL. Чтобы создать второй воркер из одного файла, можно добавить какой-нибудь параметр в URL (worker.js?num=2).

Worker можно создать и без отдельного файла. Например, так создать его из текста функции:

var code = workerFn.toString(); code = code.substring(code.indexOf("<")+1, code.lastIndexOf(">")); var blob = new Blob([code], ); worker = new Worker(URL.createObjectURL(blob)); 

Создать worker из worker-а можно только в Firefox. В Chrome можно создать shared worker из странички и передать его порт другому worker-у (об этом ниже).

Ограничения worker-ов

DOM

В worker-е нельзя использовать DOM, вместо window глобальный объект называется self. Нельзя получить доступ к localStorage и рисовать на canvas. Такие же ограничения обычно есть во всех десктопных API: доступ к окнам только из UI-треда.

Доступ к объектам

Из worker-ов нельзя вернуть объект. В javascript нет lock-ов и других возможностей потокобезопасности, поэтому из worker-ов нельзя передавать объекты по ссылке, всё отправленное в worker или из него будет скопировано.

CORS

Пока что worker-ы не поддерживают CORS, создать worker можно только загрузив его со своего домена.

Размер стека

Для worker-ов выделяется меньший размер стека, иногда это имеет значение:

Chrome/osx Firefox/osx Safari/osx Chrome/win Firefox/win IE11/win
web 20 800 48 000 63 000 41 900 51 000 63 000
worker 5 300 43 300 6 100 21 300 37 000 30 100

console

До недавнего времени не было, но обычно сейчас уже есть. В некоторых браузерах консоли в worker-ах нет, поэтому перед обращением лучше проверить её доступность.

Взаимодействие с worker-ом

После создания worker-а ему можно отправить сообщение:

worker.postMessage(); worker.onmessage = function(e) < e.data . >; sharedWorker.port.postMessage(); sharedWorker.port.onmessage = function(e) < e.data. >; 

Подписаться на сообщение в worker-е так:

// worker self.onmessage = function(e) < e.data. >; // shared worker self.onconnect = function(e) < var port = e.ports[0]; port.onmessage = function(e) < e.data. >; >; 

Аналогично и обратно, из worker-а можно вызвать или self.postMessage, или port.postMessage для shared worker-ов.

  • копировать RegExp, Blob, File, ImageData
  • восстанавливать циклические ссылки
  • Error, Function, DOM-элементы (упадёт ошибка)
  • свойства и прототипы (они не склонируются)

Transferables

Передавать по ссылке кое-что таки можно. Для этого существует второй параметр в postMessage, transferList:

var ab = new ArrayBuffer(size); worker.postMessage(< data: ab >, [ab]); 

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

Uncaught DOMException: Failed to execute 'postMessage' on 'Worker': An ArrayBuffer is neutered and could not be cloned. 

Взаимодействие двух worker-ов

В Firefox можно создать worker из worker-а (стандарт определяет subworker-ы).
Сейчас в хроме нельзя создать worker из worker-а, а иногда worker-ам надо взаимодействовать между собой. Самый простой способ — сделать передачу сообщений от одного к другому через код страницы. Но это неудобно, потому что: 1. надо писать дополнительный код, 2. в 2 раза увеличивает количество взаимодействий и копирования данных, 3. требует выполнения кода в UI-контексте.
Worker можно научить общаться с shared worker-ом, передав ему порт shared worker-а, при этом передаваемый порт в UI-контексте мы теряем; если он нужен, надо будет переподключиться к shared worker-у, создав его заново. Передача порта выглядит так:

worker.postMessage(< port: sharedWorker.port >, [sharedWorker.port]); // в worker-е поймать этот порт и сделать что-то с ним 

Правда для синхронизации всё равно движком V8 используется UI-контекст, в чём можно убедиться, завесив страничку на какое-то время: worker-ы продолжают работать, а postMessage между ними не ходят, ожидая особождения UI-контекста.

Производительность postMessage

  • dedicated worker
  • shared worker в создавшем процессе
  • shared worker в другом процессе
Chrome/osx FF/osx Safari/osx Chrome/win FF/win IE11/win
dedicated:10B 9 300 8 400 21 000 6 800 7 300 3 200
dedicated:10kB 4 000 7 000 5 000 3 000 5 000 1 800
dedicated:1MB 80 500 90 60 400 200
dedicated:10MB 8 40 7 7 52 30
dedicated:trlist:10MB 8 400 1 100 2 500 6 200 1 900 2 200
shared:10B 3 100 8 300 - 2 200 5 500 -
shared:10kB 1 800 6 900 - 1 400 4 500 -
shared:1MB 40 500 - 32 400 -
shared:10MB 4 40 - 4 53 -
shared:trlist:10MB - 260 - - 1 800 -
shared-ipc:10B 3 000 - - 2 700 - -
shared-ipc:10kB 1 600 - - 1 700 - -
shared-ipc:1MB 40 - - 30 - -
shared-ipc:10MB 4 - - 3 - -
  • затраты на взаимодействие с dedicated worker в хроме меньше, чем с shared;
  • большие объёмы данных намного быстрее передавать через transferList;
  • но всё-таки передача transferList не эквивалентна отправке ссылки или несколких байт.

Убийство worker-а

Decicated worker можно убить, вызвав worker.terminate(). С shared worker так нельзя, его выполнение будет прекращено:

  • когда он закроется сам, вызвав self.close()
  • когда закроются все странички, его использующие (при этом у worker-а не будет возможности закончить вычисления)
  • когда пользователь принудительно завершит его (например, в хроме из chrome://inspect)
  • когда упадёт или он, или процесс странички, где он живёт

Попробуем вызвать крэш процесса из shared worker-а. Вместе с worker-ом, конечно, упадёт и создавшая его вкладка. Во вкладке, где он ещё использовался, увидим такое сообщение:

К сожалению, сейчас нет штатного способа отследить закрытие worker-а или страницы, его использующей.

Учёт ресурсов в shared worker-ах в Chrome

SharedWorker живёт процессе в страницы, создавшей его. На неё учитывается и показывается в task manager CPU и память, которые потребляет worker. Если страничку закроют, её процесс с worker-ом отдаст память, используемую страницей (не сразу, через некоторое время после закрытия) и останется жить, пока другие страницы используют этот worker. Интересно, что при этом такой процесс полностью исчезнет из статистики хрома: ни память, ни CPU пользоваель не сможет отследить в его внутреннем task manager-е. Это неприятно, т.к. пользователь скорее всего не догадается, почему браузер стал потреблять так много ресурсов.

Отладка worker-ов

В chrome shared worker-ы доступны на страничке chrome://inspect/#workers:

Именно туда пишется вывод console из worker.
Dedicated worker в хроме и IE отлаживается в страничке, на которой он выполняется:

В других браузерах с отладкой worker-ов пока что плохо.

Can I Use.

Поддержка разных worker-ов на Can I Use. Коротко, применительно к сегодняшнему вебу: worker есть на современных браузерах, sharedworker — на продвинутых десктопных браузерах, serviceworker — пока что рано.

Всё написанное актуально на лето 2015 года, не забывайте, что веб быстро меняется.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *