Примечание: ниже находится частичный перевод заметки от John Resig "Computing with JavaScript Web Workers", в ней автор раскрывает особенности встроенного в браузеры механизма «отложенных» вычислений при помощи JavaScript и его будущие перспективы. Мои комментарии далее курсивом.
Последняя спецификация Web Workers, несомненно, является наиболее перспективной из грядущих нововведений в браузерах, которое позволяет исполнять JavaScript-задачи параллельно, не блокируя интерфейс браузера.
Обычно для того, чтобы добиться более-менее приемлемых вычислений при помощи JavaScript-движка, необходимо было разбивать всю работу на небольшие части, которые запускать друг за другом при помощи таймера. Это как не самый быстрый, так и совершенно не эффективный путь достижения поставленной задачи: более подробная информация приведена в статье Как работают таймеры в JavaScript.
Имея в виду текущее положение вещей, давайте углубимся в спецификацию Web Workers.
Рекомендация Web Worker частично основывается на уже проделанной работе со стороны команды Google Gears относительно модуля WorkerPool. Это идея существенно выросла и была значительно доработана, чтобы стать рекомендацией.
Worker — это скрипт, который может быть загружен и исполнен в фоновом режиме. Web Workers позволяют легко это сделать, например:
new Worker("worker.js");
В этом примере будет загружен скрипт, расположенный по адресу worker.js
, а его выполнение будет произведено в фоновом режиме (скорее всего, браузеры будут использовать встроенные средства операционной системы — нити — для реализации такого поведения).
Однако, есть несколько серьезных подводных камней:
document
, getElementById
и т.д. (Наиболее важными исключениями из этого правила будут методы setTimeout
, setInterval
и XMLHttpRequest
.)Имея в виду эти ограничения, стоит сразу задаться вопросом: а для чего, собственно, может пригодиться worker и какие задачи он способен решать?
Вы можепте использовать Worker, обмениваясь с ним сообщениями. Все браузеры (которые поддерживают данную спецификацию) позволяют обмениваться строковыми сообщениями (Firefox 3.5 также поддерживает обмен JSON-совместимыми объектами). Сообщение как может быть отослано Worker, так и Worker может ответить родительской странице таким сообщением. Ниже приведен пример обмена сообщениями.
Обмен сообщениями производится при помощи API postMessage следующим образом:
var worker = new Worker("worker.js"); // Ждем сообщений от worker worker.onmessage = function(e){ // Сообщение от клиента: e.data; }; worker.postMessage("start");
Клиент:
onmessage = function(e){ if ( e.data === "start" ) { // Выполняем какие-нибудь вычисления done(); } }; function done(){ // Отправляем полученный результат на главную страницу postMessage("done"); }
Это ограничение на передачу информации при помощи сообщений имеет под собой веские основания: это позволяет безопасно запускать дочерний скрипт (потому что, к счастью, он не может повлиять на родительский) и защищает родительский поток (обеспечение безопасности для DOM от вторжения из других потоков превратилось бы в ночной кошмар для клиентских разработчиков).
Прямо сейчас Web Workers присутствуют в Firefox 3.5 и Safari 4. Они также заявлены в последних ночных сборках Chromium. Наверное, большинство тут же подумают: что нам до возможности, которая доступна лишь для небольшой части веб-пользователей (только в двух современных браузерах!), но это не должно быть проблемой. Web Workers позволяют эффективно использовать пользовательские машины для параллельных вычислений. В такой ситуации вы можете создавать две версии своих приложений (одну для старых браузеров, и одну — для запуска через механизм Web Workers при его наличии в браузерах). В новых браузерах это просто будет работать значительно быстрее.
Некоторые занимательные примеры использования данного механизма, которые используют заявленное API.
Этот пример использует Canvas для отрисовки рассчитанной сцены. Если включить Web Workers, то заметно, как картинка отрисовывается по частям. Это происходит благодаря разбиению всей работы на части и поручение каждого набора пикселей отдельному Worker. Этот Worker затем падает массив цветов для отрисовки на Canvas, а родительская страница их применяет. (Заметьте, сам по себе Worker ничего не изменяет.)
(Требуется Firefox 3.5. Более подробно об этом примере.) В этом случае применяется несколько технологий: элемент video
, элемент canvas
и отрисовка кадров видео на сам холст. Все отслеживание движения производится в фоновом режиме при помощи Web Workers (поэтому передача видео не останавливается и не блокируется).
Этот пример пытается ограничить несколько случайных точек, используя алгоритм эмуляции огня (simulated annealing) (более подробная информация). Также здесь приведено анимированное PNG-изображение (работает в Firefox 3.5), которое вращается, пока идет вычисление в фоновом режиме.
Недавно закончилось интересное соревнование от Engine Yard. В качестве задания необходимо было подобрать фразу, которая бы давала наиболее близкий к исходному SHA1-хэш, расстояние между хэшами вычислялась по Хэммингу (число отличающихся битов).
Наибольший интерес представляло 2 участника, которые попытались решить эту задачу при помощи JavaScript (1, 2). Их программы запускались в браузере и использовали пользовательский компьютер для проведения вычислений. Скорее всего, никто из них так и не добился впечатляющего результата (если брать во внимание тех кластерных монстров, с которыми пришлось соревноваться), но сам подход вызывает неподдельный интерес.
В обоих случаях (если верить исходному коду) используется почти одна и та же тактика: рассчитываются несколько результатов, разделяемых по таймеру. Скорость подбора варьируется от браузера к браузеру на уровне 1000-1500 расчетов в секунду. Естественно, эти алгоритмы весьма ресурсоемки и полностью «убивают» пользовательский процессор и «замораживают» интерфейс браузера.
По-видимому, это является великолепной возможностью применить Web Workers!
Я взял реализацию от Ray C Morgan, вырезал весь интерфейс и таймеры и разложил вычисления на 4 параллельных потока для Web Workers. Благодаря этому удалось добиться скорости в 4500-9500 расчетов в минуту (в новых браузерах, которые поддерживают механизм Web Workers).
По этим ссылкам можно посмотреть на демонстрацию и скачать исходные коды: