HTML5: Web Workers

Автор: Topol Воскресенье, Май 6th, 2012 Нет комментариев

Рубрика: Операционные системы

В этой статье будет рассмотрена ещё одна возможность, предлагаемая HTML5 и поддерживаемая Microsoft Internet Explorer  10 Platform Preview 2, — Web Workers. Так в терминологии HTML называются средства для многопоточного выполнения Web-сценариев.

1. Введение

Сначала нам потребуется выяснить, чем эти самые Web Workers могут нам помочь. Начать выяснение придётся издалека…

1.1. Старые Web-обозреватели выполняли Web-сценарии в одном потоке

Ранее, до появления стандарта HTML5, все Web-сценарии выполнялись в одном потоке - главном потоке выполнения Web-обозревателя. Причём под словами «все Web-сценарии» подразумевались и загрузочные Web-сценарии, выполняющиеся прямо в процессе загрузки Web-страницы, и обработчики событий, и фрагменты кода, привязанные к таймерам с помощью методов setInterval и setTimeout объекта Window.

Что из всего этого следует?

Предположим, что нам требуется выполнить на стороне клиента какие-либо сложное и длительное вычисление. (Это может запросто случиться, если мы пишем развитое Web-приложение.) Мы создаём Web-сценарий, выполняющий это вычисление, и запускаем его в ответ на какое-то действие пользователя. Код выполняется, вычисление идёт, пользователь скучает…

И тут у пользователя возникает нестерпимое желание что-то сделать, скажем, прервать работу данного Web-сценария. Он нажимает предусмотренную нами кнопку Прервать. И что же? Web-обозреватель и ухом не поведёт! Более того, даже сама эта кнопка при щелчке на ней просто не нажмётся!

Как уже говорилось, выполнение всех Web-сценариев производится в главном потоке выполнения Web-обозревателя. Но в этом же потоке выполняется обработка и всех действий пользователя: прокрутка Web-страниц, переходов по гиперссылкам, нажатие кнопок и др. И одновременно выполнять Web-сценарий и обрабатывать действия пользователя Web-обозреватель не может. Поэтому, пока выполняется наш Web-сценарий, Web-обозреватель не сможет обработать щелчок на кнопке — ему придётся ждать, пока Web-сценарий не завершит свою работу.

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

Таких выходов существуют два.

Первый — переписать Web-сценарий таким образом, чтобы он выполнял вычисление по частям, и привязать его к таймеру с помощью метода setInterval объекта Window. В этом случае при очередном срабатывании таймера Web-сценарий выполнит очередной проход вычисления и завершится, освободив главный поток для обработки действий пользователя и выполнения других Web-сценариев. Web-приложение станет отзывчевее.

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

Недостатков же у него побольше. И во многих случаях они могут оказаться критическими.

  • Нам придётся каким-то образом хранить состояние процесса вычисления, возникшее на момент завершения очередного прохода вычислений. Соответственно, при последующем запуске Web-сценария это состояние потребуется восстановить и уже от него начинать очередную «пляску».
  • Не все задачи, даже чисто математические, могут быть выполнены по частям.
  • Даже если мы и реализуем этот подход, наш Web-сценарий всё равно будет выполняться в главном потоке, следовательно, во время его выполнения Web-обозреватель больше ничего сделать не сможет.

1.2. Web Workers как средство выполнить Web-сценарий в другом потоке

Поэтому предпочтительнее воспользоваться вторым выходом из описанного ранее положения — использованием Web Workers.

Web Worker (множественное число — Web Workers) — это Web-сценарий, работающий в параллельном потоке выполнения. Этот поток имеет более низкий приоритет, нежели главный, поэтому в любом случае не сможет нарушить это работу; следовательно, задачи обработки действий пользователя и реакции на них всегда будут выполняться незамедлительно. Такие низкоприоритетные потоки называются фоновыми.

Поддержка Web Workers появилась в HTML5. На данный момент они успешно обрабатываются Internet Explorer 10 Platform Preview 2 и многими из конкурирующих Web-обозревателей.

Достоинства Web Workers перечислены далее.

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

Недостатки, к сожалению, у нах тоже есть…

  • Web Worker не может обращаться к Web-странице, чтобы, скажем, что-то изменить на ней. Дабы сделать это, нам придётся написать Web-сценарий, который будет выполняться в главном потоке, принимать от Web Worker особые сообщения (о них мы обязательно поговорим) и в ответ на них вносить изменения в Web-страницу.
  • Web Worker также не может обращаться к переменным, объявленным в общем потоке. Это также придётся реализовывать с помощью обмена сообщениями.
  • Web Workers поддерживаются только Web-обозревателями, «знакомыми» с HTML5. Впрочем, такие Web-обозреватели сейчас составляют большинство на рынке.

Именно о Web Workers и пойдёт речь в данной статье.

2. Реализация простейшего Web Worker

Рассмотрим, как реализуется самый простой Web Worker, который только выполняет вычисление и отправляет его результаты Web-сценарию, работающему в главном потоке.

2.1. Создание Web Worker

Web Worker реализуется в виде обычного файла Web-сценария с расширением js. Этот файл должен включать в себя исключительно JavaScript-код самого Web-сценария; вставки кода HTML и CSS в нём не допускаются.

Web Worker должен содержать код, собственно выполняющий задачу, ради которой он и создавался.

В Web Worker не допускается код, обращающийся к Web-странице и её элементам. Дело в том, что в контексте Web Worker не существует переменная document, хранящая экземпляр объекта HTMLDocument, который представляет Web-страницу и через который Web-сценарий может добраться до её элементов. Кроме того, значение этой переменной невозможно передать Web Worker с помощью сообщения — это значение не будет иметь в коде Web Worker никакого смысла.

По этой же причине в Web Workers не допускается код, обращающийся к окну Web-обозревателя. В контексте Web Worker просто отсутствует переменная window, где хранится экземпляр объекта Window, представляющий это окно. (Что вполне логично, так как Web Worker выполняется вне окна Web-обозревателя.)

2.2. Создание запускающего Web-сценария

Web Worker сам по себе на выполнение запущен не будет. Мы сами должны сделать это.

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

Чтобы запустить Web Worker на выполнение, запускающий Web-сценарий должен создать экземпляр объекта Worker, представляющий данный Web Worker. Формат конструктора этого объекта приведён ниже.

Код:
new Worker([i]<интернет-адрес файла Web-сценария>[/i])

В качестве единственного параметра конструктору передаётся строка с интернет-адресом файла Web-сценария, хранящего код Web Worker.

Экземпляр объекта, возвращённый конструктором, следует сохранить в какой-либо переменной. Он пригодится, когда мы будем привязывать к Web Worker слушатель сообщений, полученных от Web Worker, и отправлять сообщения ему.

Код:
var orjWW = new Worker(«ww.js»);

Здесь мы запускаем на выполнение Web Worker, код которого хранится в файле ww.js, находящемся в той же папке, что и файл самой Web-страницы. Полученный экземпляр объекта Worker мы сохраняем в переменной objWW.

2.3. Реализация передачи сообщений

Как говорилось ранее, Web Worker не имеет доступа ни к Web-странице, ни к переменным, объявленным в главном потоке. Так что мы не сможем обычным путём ни вывести результаты работы на Web-страницу, ни передать их другому Web-сценарию.

Единственный способ сделать это — использовать механизм сообщений. C их помощью мы можем передавать данные как из Web Worker в Web-сценарий, работающий в главном потоке выполнения, так и в обратном направлении.

2.3.1. Передача сообщений из главного потока в Web Worker

Сначала рассмотрим передачу сообщений из Web-сценария, выполняющегося в главном потоке, в Web Worker.

Объект Worker, представляющий в главном потоке Web Worker, поддерживает метод postMessage, который и выполняет передачу сообщения. Формат его вызова очень прост.

Код:
[i]<Web Worker>[/i].postMessage([i]<передаваемое значение>[/i])

В качестве единственного параметра этому методу передаётся значение, которое должно быть передано в Web Worker в составе сообщения. Это значение может быть любого типа: строкой, числом, логической величиной и даже экземпляром объекта.

Внимание!
Допускается передача в составе сообщения только экземпляров объектов языка JavaScript: String, Number, Array, Date, Object и др. Экземпляры объектов Web-обозревателя (Window, Navigator и пр.) и объектов, представляющих элементы Web-страниц, передавать не допускается, так как в контексте Web Worker они не будут иметь смысла.

Результата метод postMessage не возвращает.

Код:
objWW.postMessage(«start»);

Передаём запущенному ранее Web Worker, хранящемуся в переменной objWW, строку «start».

Код:
var obj = { n1: 45, n2: 78.3; flag: false };
objWW.postMessage(obj);

А здесь мы передаём в составе сообщения экземпляр объекта Object, хранящий три значения в своих свойствах. Это, кстати, единственный способ передать в составе сообщения сразу несколько значений.

2.3.2. Передача сообщений из Web Worker в главный поток

Теперь выясним, как выполняется передача сообщений в обратном направлении — из Web Worker в Web-сценарий, работающий в главном потоке выполнения.

Web Worker в «своём» потоке представляется в виде экземпляра другого объекта — DedicatedWorkerGlobalScope. Этот объект также поддерживает метод postMessage, знакомый нам по предыдущему параграфу и ведущий себя точно так же.

Код:
postMessage([i]<передаваемое значение>[/i])

Здесь у читателей может возникнуть вопрос: а где указание экземпляра объекта, чей метод postMessage вызывается? Дело в том, что код Web Worker выполняется в контексте экземпляра объекта DedicatedWorkerGlobalScope, который его представляет. Это значит, что для вызова свойств и методов данного экземпляра объекта нам не требуется указывать сам этот экземпляр — Web-обозреватель и так «поймёт», кому принадлежит вызываемое нами свойство или метод.

Код:
postMessage(«started»);

Пересылаем главному потоку строку «started».

Код:
var obj = { res: 207.94, success: true };
objWW.postMessage(obj);

Пересылаем главному потоку экземпляр объекта Object, хранящий два значения в своих свойствах.

2.4. Реализация получения сообщений

Так, посылать сообщения мы научились. Настала пора узнать, как получать их и извлекать хранящиеся в них данные.

2.4.1. Получение сообщений в главном потоке

Раз уж мы начали изучение передачи сообщений с главного потока, то и получение их сначала рассмотрим также в главном потоке.

Объект Worker поддерживает свойство onmessage. Этому свойству присваивается функция, которая станет обработчиком события message, возникающего при получении сообщения от Web Worker. Такие функции, выполняющие обработку сообщений, которые могут приходить в произвольные моменты времени, называются слушателями.

Код:
objWW.onmessage = function(evt)
{
[i]<код, выполняющий обработку полученных сообщений>[/i]
}

Функция — обработчик события message должна принимать один параметр (на приведённом выше примере он обозначен как evt). Этим параметром станет экземпляр объекта MessageEvent, представляющий сведения о полученном сообщении. (Автору не удалось выяснить точное имя объекта, представляющего эти сведения, поэтому он назвал его «от балды».)

Из всех свойств, поддерживаемых объектом MessageEvent, нас интересует только свойство data. Оно хранит значение, полученное с данным сообщением. (Возможно, этот объект поддерживает и другие свойства и методы, но автору не удалось найти никаких сведений об этом.)

Код:
objWW.onmessage = function(evt)
{
var objValue = evt.data;
var iRes = objValue.res;
var bSuccess = objValue.success;
. . .
}

Получаем экземпляр объекта Object, принятый с сообщением, и извлекаем значения из его свойств res и success.

2.4.2. Получение сообщений в Web Worker

Получение сообщений в Web Worker выполняется точно так же, как и в главном потоке.

Мы знаем, что код Web Worker выполняется в контексте экземпляра объекта DedicatedWorkerGlobalScope, представляющего этот Web Worker. Данный объект также поддерживает свойство onmessage, знакомое нам по предыдущему параграфу.

Код:
onmessage = function(evt)
{
var objValue = evt.data;
var iN1 = objValue.n1;
var iN2 = objValue.n2;
var bFlag = objValue.flag;
. . .
}

Получаем экземпляр объекта Object, принятый с сообщением, и извлекаем значения из его свойств n1, n2 и flag.

2.5. Использование таймеров

Объект DedicatedWorkerGlobalScope поддерживает методы setInterval, setTimeout, clearInterval и clearTimeout, предназначенные для запуска и уничтожения таймеров Web-обозревателя. Так что мы можем использовать в Web Workers таймеры.

Код:
setInterval(
function()
{
. . .
}, 1000);

2.6. Пример реализации простейшего Web Worker

Для закрепления полученных знаний давайте создадим небольшой пример, где задействуем простейший Web Worker. Этот пример будет состоять из двух частей.

Часть первая — небольшая Web-страница, которая будет выводить целые числа, начиная с 0. Вычисляться эти числа будут в Web Worker (который станет второй частью примера).

Часть вторая, как уже говорилось, — Web Worker, который будет вычислять эти числа. Чтобы создать видимость сложного и длительного процесса вычисления, мы привяжем код, который собственно и будет производить вычисления, к таймеру, срабатывающему каждую секунду. А сам процесс вычисления будет заключаться в увеличении очередного числа на единицу и передаче его Web-странице, которая выведет полученное число на экран (первая часть примера).

Сначала приведём код Web-страницы.

Код:
<!DOCTYPE html>
<HTML>
<HEAD>
<META HTTP-EQUIV=»Content-Type» CONTENT=»text/html; charset=utf-8″>
<TITLE>Простейший Web Worker</TITLE>
</HEAD>
<BODY>
<P ID=»parOutput»></P>
<SCRIPT>
var objParOutput = document.getElementById(«parOutput»);

var objWorker = new Worker(«1.js»);

objWorker.onmessage = function(evt)
{
objParOutput.innerHTML = evt.data;
}
</SCRIPT>
</BODY>
</HTML>

Для вывода полученный в результате «сложных» вычислений чисел мы создали абзац с именем parOutput.

В Web-сценарии мы сначала получаем доступ к абзацу parOutput, воспользовавшись методом getElementById объекта HTMLDocument. Этот метод принимает строку с именем элемента Web-страницы, заданным с помощью атрибута тега ID, и возвращает экземпляр объекта, представляющего данный элемент.

Далее мы запускаем на выполнение Web Worker, производящий вычисления. В нашем примере он хранится в файле 1.js.

Далее мы задаём для сообщений, которые будем получать от Web Worker, функцию-слушатель. Единственное действие, которое она будет выполнять, — вывод полученного числа в абзаце parOutput.

Все объекты, представляющие элементы Web-страницы, которые могут иметь содержимое (в том числе и абзацы), поддерживают свойство innerHTML. Оно хранит строку с HTML-кодом, создающим содержимое данного элемента. Причём свойство это доступно и для чтения, и для записи.

Чтобы задать для абзаца parOutput новое содержимое, мы просто присваиваем полученное от Web Worker число его свойству innerHTML.

Сохраним данную Web-страницу в файле с именем, скажем, 1.html. И не забудем задать для неё кодировку UTF-8. (Эта кодировка рекомендуется к использованию во всех Web-страницах, созданных на языке HTML5.)

А вот код Web Worker:

Код:
var iValue = 0;

setInterval(
function()
{
iValue++;
postMessage(iValue);
}, 1000);

Здесь мы сначала объявляем переменную iValue, в которой будет храниться текущее вычисленное число. Далее мы привязываем к таймеру, срабатывающему каждую секунду, функцию, которая будет выполнять инкремент (увеличение на единицу) данного числа и отсылку его главному потоку в составе сообщения.

Сохраним данный Web-сценарий в файле 1.js, также задав для него кодировку UTF-8.

После этого откроем Web-страницу 1.html в Internet Explorer 10 Platform Preview 2 и полюбуемся на сменяющие друг друга числа.

3. Более сложные возможности Web Workers

А теперь рассмотрим более сложные и развитые возможности Web Workers, которые нам вполне могут пригодиться.

3.1. Завершение выполнения Web Worker

Часто возникают ситуации, когда требуется завершить работу Web Worker. Это может случиться, скажем, если Web Worker уже завершил процесс вычислений, и следует выгрузить его их памяти, чтобы освободить её для других задач.

Объект DedicatedWorkerGlobalScope, представляющий Web Worker в его собственном потоке, поддерживает метод close. Этот метод немедленно завершает работу Web Worker и выгружает его из памяти. Параметров он не принимает и результата не возвращает.

Особо отметим, что завершить работу Web Worker можно только из его кода и из его потока. Из главного потока это сделать нельзя. (Хотя, надо сказать, что станларт HTML5 предусматривает средства сделать это, но текущая реализация Web Workers в Internet Explorer 10 Platform Preview 2 их не поддерживает).

Однако из главного потока можно послать Web Worker с сообщением особое значение, получив которое, он завершит свою работу. Реализовать это можно, например, так:

Код:
onmessage = function(evt)
{
switch (evt.data) {
. . .
case «stop»:
close();
break;
. . .
}
}

Здесь мы останавливаем работу Web Worker в ответ на получение им сообщения со строковым значением «stop».

3.2. Подключение к Web Worker других файлов Web-сценария (библиотек)

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

Если мы создаём Web-сценарий в самой Web-странице, то есть в главном потоке, то можем элементарно подключить файл Web-сценария, содержащий библиотеку, с помощью тега <SCRIPT>. Но как сделать это в коде Web Workers?

С помощью метода importScripts объекта DedicatedWorkerGlobalScope. Он как раз и выполняет подключение сторонних файлов Web-сценариев.

Код:
importScripts([i]<интернет-адреса подключаемых файлов>[/i])

В качестве параметров этот метод принимает строки, содержащие интернет-адреса подключаемых файлов; причём таких параметров он может принять сколько угодно. Результата он не возвращает.

При подключении каждого файла Web-сценария хранящийся в нём код будет выполнен. Причём выполняться они будут в том порядке, в котором в вызове метода importScripts присутствуют параметры, содержащие их интернет-адреса.

Предположим, мы сохранили в файле Web-сценария library.js код, объявляющий функцию someFunction. Теперь, чтобы успешно её использовать в Web Worker, мы должны сначала подключить к нему этот файл:

Код:
importScripts(«library.js»);
someFunction();
. . .

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

Код:
importScripts(«library1.js», «library2.js»);

3.3. Получение сведений о местоположении файла Web Worker

Иногда может оказаться полезной возможность получения в Web Worker сведений о местоположении файла, в котором хранится его код; в частности, такая потребность может возникнуть при вычислении интернет-адресов библиотечных файлов Web-сценариев, которые требуется подключить к нему. Эти сведения включают в себя интернет-адрес файла и различные его части.

Объект DedicatedWorkerGlobalScope поддерживает доступное только для чтения свойство location. Оно хранит экземпляр объекта WorkerLocation, хранящий то, что нам нужно, — сведения о местоположении файла Web Worker.

Код:
var objLocation = location;

Объект WorkerLocation поддерживает следующие свойства:

  • hash — возвращает часть интернет-адреса, следующую за символом решётки (#). Если интернет-адрес не содержит этой части, возвращается пустая строки.
  • host — возвращает имя хоста и номер порта (если он указан), разделённые символом двоеточия.
  • hostname — возвращает имя хоста.
  • href — возвращает полный интернет-адрес.
  • pathname — возвращает путь к файлу.
  • port — возвращает номер порта. Если порт не указан, возвращается номер порта по умолчанию, используемый данным протоколом (например, протокол HTTP использует порт по умолчанию 80, протокол FTP — 21).
  • protocol — возвращает обозначение протокола с завершающим символом двоеточия.
  • search — возвращает часть интернет-адреса, следующую за вопросительным знаком. Если интернет-адрес не содержит этой части, возвращается пустая строка.

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

Код:
var sHostName = objLocation.hostname;
var sPath = objLocation.pathname;

3.4. Получение сведений о Web-обозревателе

Ещё нам может пригодиться возможность получить в Web Worker сведения о Web-обозревателе. Это данные о самой программе Web-обозревателя и признак того, работает ли Web-обозреватель в автономном (offline) режиме.

Объект DedicatedWorkerGlobalScope поддерживает доступное только для чтения свойство navigator. Оно хранит экземпляр объекта WorkerNavigator, хранящий сведения о Web-обозревателе.

Код:
var objNavigator = navigator;

Объект WorkerNavigator поддерживает следующие свойства:

  • appName — возвращает название программы Web-обозревателя, например «Microsoft Internet Explorer».
  • appVersion — возвращает обозначение версии программы Web-обозревателя.
  • onLine — возвращает true, если Web-обозреватель работает в сети, и false, если он находится в автономном (offline) режиме.
  • platform — возвращает обозначение платформы, на которой работает Web-обозреватель, например, «Win32″ (32-разрядная Windows).
  • userAgent — возвращает обозначение Web-обозревателя, передаваемое им в составе клиентских запросов Web-серверу.

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

Код:
if (objNavigator.onLine)
. . .

Здесь мы проверяем, работает ли Web-обозреватель в сети, и, если это так, выполняем какие-либо действия.

3.5. Обработка ошибок, возникающих в Web Worker

А вот функция обработки ошибок, что могут возникнуть в Web Worker, может оказаться полезной однозначно.

Объект Worker, представляющий Web Worker в главном потоке, поддерживает свойство onerror. Этому свойству присваивается функция, которая станет обработчиком события error, генерируемого при возникновении в Web Worker ошибки. Фактически это тоже функция-слушатель.

Код:
objWW.onerror = function(evt)
{
[i]<код, выполняющий обработку ошибок Web Worker>[/i]
}

Функция — обработчик события error должна принимать один параметр (на приведённом выше примере он обозначен как evt). Этим параметром станет экземпляр объекта ErrorEvent, представляющий сведения о возникшей ошибке.

Объект ErrorEvent поддерживает следующие доступные только для чтения свойства:

  • filename — возвращает полный интернет-адрес файла Web Worker, в котором возникла ошибка, в виде строки.
  • lineno — возвращает номер строки кода, в которой возникла ошибка, в виде целого числа.
  • message — возвращает описание возникшей ошибки в виде строки.
Код:
objWW.onerror = function(evt)
{
var sURI = evt.filename;
var iLine = evt.lineno;
. . .
}

Получаем интернет-адрес файла Web Worker, в котором возникла ошибка, и номер вызвавшей эту ошибку строки кода.

Свойство onerror поддерживает и объект DedicatedWorkerGlobalScope. Так что мы можем обрабатывать возникающие ошибки прямо в коде Web Worker.

Код:
onerror = function(evt)
{
var sURI = evt.filename;
var iLine = evt.lineno;
. . .
}

3.6. Пример реализации более сложного Web Worker

Напоследок рассмотрим пример более сложного Web Worker. Создадим его на основе примера, описанного в параграфе 2.6.

Мы добавим на Web-страницу, которая выводит результаты работы Web Worker, Web-форму с двумя кнопками. Кнопка Старт будет запускать Web Worker на выполнение, а кнопка Стоп - останавливать.

Сначала рассмотрим код Web-страницы.

Код:
<!DOCTYPE html>
<HTML>
<HEAD>
<META HTTP-EQUIV=»Content-Type» CONTENT=»text/html; charset=utf-8″>
<TITLE>Более сложный Web Worker</TITLE>
</HEAD>
<BODY>
<P ID=»parOutput»></P>
<FORM>
<INPUT TYPE=»button» ID=»btnStart» VALUE=»Старт»>
<INPUT TYPE=»button» ID=»btnStop» VALUE=»Стоп» DISABLED>
</FORM>
<SCRIPT>
var objParOutput = document.getElementById(«parOutput»);
var objBtnStart = document.getElementById(«btnStart»);
var objBtnStop = document.getElementById(«btnStop»);

var objWorker;

objBtnStart.addEventListener(«click»,
function()
{
objWorker = new Worker(«2.js»);

objWorker.onmessage = function(evt)
{
objParOutput.innerHTML = evt.data;
}

objBtnStart.disabled = true;
objBtnStop.disabled = false;
}, false);

objBtnStop.addEventListener(«click»,
function()
{
objWorker.postMessage(«stop»);

objBtnStart.disabled = false;
objBtnStop.disabled = true;
}, false);
</SCRIPT>
</BODY>
</HTML>

Дадим необходимые пояснения.

Кнопку Стоп мы сделали изначально недоступной (в самом деле, нельзя остановить выполнение Web Worker, который ещё не запущен). Для этого мы вставили в создающий данную кнопку тег <INPUT> атрибут без значения DISABLED.

Событие click возникает в элементе Web-страницы, в том числе и кнопке, при щелчке на нём мышью.

Метод addEventListener, поддерживаемый всеми объектами, что представляют элементы Web-страницы, служит для привязки обработчика к событию. Такой способ привязки обработчиков рекомендуется использовать во всех Web-страницах, созданных с применением языка HTML5. Первым параметром этот метод принимает строку с именем события, вторым — собственно функцию, которая станет обработчиком, а третьим — логическую величину, указывающую, будет ли событие обрабатываться на стадии захвата (true) или же на стадии всплытия (false).

Мы привязываем к кнопке Старт обработчик события click, который запустит Web Worker на выполнение и задаст для него функцию-слушатель, которая будет обрабатывать полученные от Web Worker сообщения. Эта часть кода нам уже знакома по примеру из параграфа 2.6.

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

Доступностью элемента управления для посетителя управляет свойство disabled, поддерживаемое всеми объектами, что представляют элементы управления. Значение false, присвоенное данному свойству, делает элемент управления доступным, а значение true — недоступным.

А к кнопке Стоп мы привязываем другой обработчик события click. Он посылает Web Worker сообщение со строкой «stop», сигнализируя тем самым, чтобы он завершил свою работу. Также он делает кнопку Старт доступной, а кнопку Стоп - недоступной. Теперь посетилель сможет снова запустить Web Worker.

Сохраним Web-страницу в файле с именем, скажем, 2.html, задав для неё кодировку UTF-8.

Код Web Worker приведён ниже.

Код:
var iValue = 0;

setInterval(
function()
{
iValue++;
postMessage(iValue);
}, 1000);

onmessage = function(evt)
{
if (evt.data == «stop»)
close();
}

Здесь всё нам уже знакомо. Единственное — добавилась функция-слушатель сообщений. Она получает от главного потока значение, проверяет, является ли оно строкой «stop», и, если это так, завершает работу Web Worker.

Сохраним код Web Worker в файле 2.js в кодировке UTF-8.

После этого откроем Web-страницу 2.html в Internet Explorer 10 Platform Preview 2. Нажмём кнопку Старт и понаблюдаем, как меняются числа. Остановим вычисление нажатием кнопки Стоп. Повторим эти действия несколько раз, дабы удостовериться, что всё работает нормально.

Источник: thevista.ru

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты