Просто обычная практика выполнения клиентских сценариев в единственном потоке хороша ровно до того момента, пока не надо произвести действительно серьезных и тяжелых вычислений.
Разумеется, подобных ситуаций не создавалось в те времена, когда призванием JavaScript-сценария было подсветить активную кнопку или красиво отобразить всплывающую подсказку. Но со временем задачи перед клиентскими сценариями встают все серьезней и серьезней, а блокировать процесс работы с веб-страницей, пока выполняется, например, полутораминутная обработка изображения перед загрузкой, совсем не хочется. И наблюдать js-сообщения вроде показанного на рис. 100 — согласитесь, тоже не самое приятное занятие. И вот тут на помощь приходят наши веб-работники, проводящие подобные «тяжелые» операции в режиме фоновых вычислений.
В данном случае под фоновыми вычислениями мы подразумеваем код, выполняющийся вне основного потока выполнения кода вебстраницы, еще конкретнее — вне основного потока браузера. Для
Рис. 100. Знакомо?
Реализации этой модели сначала создадим экземпляр фоновой обработки (Worker):
Var worker; if (!window. Worker){
Alert(‘WebWorkers не подерживаются!); } else {
Worker = new Worker(‘webWorker. js’);
}
Теперь создадим файл webWorker. js со следующим содержанием:
Onmessage = function(e){ Message("Hi!");
}
Что тут происходит, станет ясно чуть позже.
Если все в порядке и ваш браузер достаточно современен, то «работник» уже создан и готов к работе. Теперь ему можно давать задания. Как? С помощью уже знакомого нам механизма веб-сообщений Web Messaging API:
Worker. essage(‘Hello!’);
Таким образом можно посылать сообщения фоновому процессу. Соответственно, сообщения от worker сможет читать функция обратного вызова, ожидающая связи:
Worker. onmessage = function(event){
Alert("Получено сообщение: "+event. data);
Испытаем механизм в действии (рис. 101).
Рис. 101. Привет от Worker
Посылать фоновому процессу и принимать от него можно только текстовые сообщения, чего в принципе достаточно — формат JSON поможет нам оперировать более высокими материями.
Как мы видим из кода webWorker. js, он оперирует точно таким же API. Можно сделать его немножко «умнее»:
Onmessage = function(e){ if(e. data == "Hello!"{ essage("Hi!");
} else {
Age("Well… And where ‘Hello!’?");
}
Отлично, коду worker доступен Wqeb Mesage API! А что еще? Наверное, и все остальные возможности полноценного JavaScript-сценария?
К сожалению, придется немого разочаровать — доступно далеко не все. Что делать, с правами у рабочего класса все еще не очень хорошо. Прежде всего ограничения касаются доступа к Document Object Model (DOM). Это, в частности, обозначает, что доступа к коллекциям вроде document.* или window.* нет, и это серьезное ограничение. Единственное исключение — объект navigator. Что же нам остается?
В число прав рабочих, кроме вышеупомянутых onmessage и age, входит возможность прекращать работу — метод close(), использовать таймеры (setTimeout, setInterval), использовать объект XMLHTTPRequest, работать с объектом location (определяя свое местоположение).
Также worker доступны все нативные функции JavaScript (включая eval()) и новые возможности веба — WebSockets и Web-базы данных.
Как видите, возможностей хватает, но приходится оговориться — все это перечислено в спецификации. В жизни пока реализовано далеко не все. Впрочем, не будем о грустном.
Еще две возможности, реализованные в WebWorkers, обеспечивают базовые возможности масштабирования. Во-первых, это возможность загружать сторонние сценарии и библиотеки с помощью функции import-Scripts, во-вторых, самим использовать фоновые вычисления.
Теперь попробуем сотворить с помощью worker что-нибудь условно полезное. Например, заставим их вычислять в фоновом режиме жизненно необходимые в быту числа Фибоначчи. Для этого сделаем небольшую разметку:
<html>
<head>
<meta charset="cp1251">
<title>WebWorkers API</title>
<script src="../jquery-1.4.4.min. js"></script>
<script> var worker;
Worker = new Worker(‘webWorker. js’); function getNumber(){ worker. age(‘get’);
}
Worker. onmessage = function(event){ alert(event. data);
}
</script>
</head>
<body>
<img src = "Fibonacci2.jpg">
<br>
<input type = "button" onclick = "getNumber();" value = "Получить число"> </body>
</html>
Код ‘webWorker. js’ теперь будет таким:
Var current = 1; var prev = 0: function getNumbers(){ next = current+prev: age(next): prev = current; current=next:
}
Onmessage = function(e){ if(e. data == "get"){ getNumbers():
}
}
Тут мы начали сразу с третьего числа, но я думаю, что Леонардо Пизански нас простит. Гораздо важнее показать, что мы перенесли часть нашего JavaScript-сценария в фон. Щелкаем по кнопке и наблюдаем за возрастающими цифрами — все работает! (рис. 102).
Рис. 102. Фибоначчи одобряет
Впрочем, настоящей работы в фоне мы тут не видим. Давайте исправим это положение. Пусть посетитель страницы будет занят каким-нибудь делом — например, пишет статью о средневековом математике, а цифры появляются сами по себе (для вдохновения пишущего).
Сначала сверстаем интерфейс:
<div style = "text-align: left;widht=300px;">
<img src = "Fibonacci2.jpg"><textarea ></textarea>
<br>
<div id="info"></div>
<input type = "button" onclick = "startGetNumber();" value = "Начать фибонач-чить!">
<input type = "button" onclick = "resetNumber();" value = "Reset">
<input type = "button" onclick = "stop();" value = "Stop">
<br>
<input type = "button" onclick = "terminate()" value = "Kill Worker!">
</div>
Первая кнопка у нас будет запускать выдачу чисел последовательности Фибоначчи, вторая перезапускает выдачу этих чисел сначала, третья останавливает последовательность. Реализация данных действий в основном потоке будет следующей:
Worker = new Worker(‘webWorker. js’);
Function startGetNumber(){ worker. ge(‘ start’);
}
Function resetNumber(){ worker. ge(‘ reset’);
}
Function stop(){ worker. ge(‘ stop’);
}
Worker. onmessage = function(event){ $( ‘#info’).html(event. data);
}
То есть мы просто посылаем команды нашему работнику, чтобы тот вкалывал. Сам файл webWorker. js’ примет следующий вид:
Var stop = 0; var current = 1; var prev = 0;
Function getNumbers(){
Next = current+prev;; prev = current; current=next; ge(next); if(stop!= 1){
SetTimeout("getNumbers()", 1000);
}
}
Onmessage = function(e){ if(e. data == "start"){ stop = 0; getNumbers();
}
If(e. data == "reset"){ current = 1; prev = 0;
}
If(e. data == "stop"){ stop = 1;
}
}
Тут тоже все просто — функция обратного вызова оперирует глобальными переменными, управляя работой функции getNumbers. Для полноты добавим еще одну функцию-обработчик:
Function terminate(){ worker. terminate():
}
Метод terminate в соответствии со своим названием попросту уничтожает объект Worker. Результат — на рис. 103.
Добавить комментарий