Одна из основных особенностей клиентских сценариев на языке JavaScript заключается в том, что они выполняются в единственном потоке выполнения: броузер, к примеру, никогда не будет выполнять два обработчика событий одновременно, и таймер никогда не сработает, пока выполняется обработчик события. Параллельные обновления переменных приложения или документа просто невозможны, и программистам, разрабатывающим клиентские сценарии, не требуется задумываться или хотя бы понимать особенности параллельного программирования. Как следствие, функции в клиентских сценариях на языке JavaScript должны выполняться очень быстро, иначе они остановят работу цикла событий и веб-броузер перестанет откликаться на действия пользователя. Именно по этой причине прикладной интерфейс поддержки Ajax всегда действует асинхронно, и по этой же причине в клиентском JavaScript отсутствуют синхронные версии функций load или require для загрузки библиотек на языке JavaScript.
Спецификация «Web Workers» [58] со всеми мерами предосторожности ослабляет ограничение на единственный поток выполнения в клиентском JavaScript. «Фоновые потоки», определяемые спецификацией, фактически являются параллельными потоками выполнения. Однако эти потоки выполняются в изолированной среде, не имеют доступа к объектам
Window
и
Document
и могут взаимодействовать с основным потоком выполнения только посредством передачи асинхронных сообщений. Это означает, что параллельные изменения дерева DOM по-прежнему невозможны, но это также означает, что теперь имеется возможность использовать синхронные прикладные интерфейсы и писать функции, выполняющиеся длительное время, которые не будут останавливать работу цикла событий и подвешивать броузер. Создание нового фонового потока выполнения не является такой тяжеловесной операцией, как открытие нового окна броузера, но также не является легковесной операцией, вследствие чего нет смысла создавать новые фоновые потоки для выполнения тривиальных операций. В сложных веб-приложениях может оказаться полезным применение даже нескольких десятков фоновых потоков, но весьма маловероятно, что приложения с сотнями и тысячами таких потоков смогут иметь практическую ценность.
58
Спецификация «Web Workers» изначально была частью спецификации HTML5, но затем была выделена в отдельный, независимый, хотя и тесно связанный со стандартом, документ. На момент написания этих строк проект спецификации был доступен по адресами http://whatwg.org/ww.
Как и любой прикладной интерфейс поддержки многопоточных приложений, спецификация «Web Workers» определяет две различные части. Первая - объект
Worker
, который представляет фоновый поток выполнения в программе, создавшей его. Вторая - объект
WorkerGlobalScope
, глобальный объект нового фонового потока выполнения, который представляет фоновый поток выполнения внутри него самого. Оба объекта описываются в следующих подразделах. За ними следует раздел с примерами.
22.4.1. Объект Worker
Чтобы создать новый фоновый поток, достаточно просто вызвать конструктор
Worker,
передав ему URL-адрес, определяющий программный код на языке JavaScript, который должен выполняться в фоновом потоке:
var loader = new Worker("utils/loader.js");
Если указать относительный URL-адрес, он будет интерпретироваться относительно URL-адреса документа, содержащего сценарий,
который вызвал конструктор
Worker
. Если указать абсолютный URL-адрес, он должен иметь то же происхождение (протокол, имя хоста и порт), что и вмещающий документ.
После создания объекта
Worker
ему можно отправлять данные с помощью его метода
postMessage.
Значение, переданное методу
postMessage,
будет скопировано (смотрите врезку «Структурированные копии» выше), и полученная копия будет передана фоновому потоку вместе с событием «message»:
loader.postMessage("file.txt");
Обратите внимание, что, в отличие от метода
postMessage
объекта
Window
, метод
postMessage
объекта
Worker
не имеет аргумента, в котором передавалась бы строка, описывающая происхождение (раздел 22.3). Кроме того, метод
postMessage
объекта
Worker
корректно копирует сообщение во всех текущих броузерах, в отличие от
Window
.
postMessage,
который в некоторых основных броузерах способен принимать только строковые сообщения.
Принимать сообщения от фонового потока можно с помощью обработчика события «message» объекта
Worker
:
worker.onmessage = function(e) {
var message = e.data; // Извлечь сообщение
console.log("Содержимое: " + message); // Выполнить некоторые действия
}
Если фоновый поток возбудит исключение и не обработает его, это исключение продолжит распространение в виде события, которое также можно перехватить:
worker.onerror = function(e) {
// Вывести текст ошибки, включая имя файла фонового потока и номер строки
Подобно всем объектам, в которых могут возбуждаться события, объект
Worker
определяет стандартные методы
addEventListener
и
removeEventListener,
которые можно использовать вместо свойств
onmessage
и
onerror
, если необходимо установить несколько обработчиков событий.
Объект
Worker
имеет еще один метод,
terminate,
который останавливает выполнение фонового потока.
22.4.2. Область видимости фонового потока
При создании нового фонового потока с помощью конструктора
Worker
вы задаете URL-адрес файла с программным кодом на языке JavaScript. Этот программный код выполняется в новой, нетронутой среде выполнения JavaScript, полностью изолированной от сценария, запустившего фоновый поток. Глобальным объектом этой изолированной среды выполнения является объект
WorkerGlobalScope
. Объект
WorkerGlobalScope
– это чуть больше, чем просто глобальный объект JavaScript, но меньше, чем полноценный клиентский объект
Window
.
Объект
WorkerGlobalScope
имеет метод
postMessage
и свойство обработчика события
onmessage
, подобные своим аналогам в объекте
Worker
, но действующие в обратном направлении. Вызов метода
postMessage
внутри фонового потока сгенерирует событие «message» за его пределами, а сообщения, отправляемые извне, будут превращаться в события и передаваться обработчику
onmessage
. Обратите внимание: благодаря тому, что объект
WorkerGlobalScope
является глобальным объектом для фонового потока, метод
postMessage
и свойство
onmessage
воспринимаются программным кодом, выполняющимся в фоновом потоке, как глобальная функция и глобальная переменная.