Библиотека сайта rus-linux.net
Processing.js
Глава 17 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: Processing.js
Автор: Mike Kamermans
Перевод: А.Панин
17.2. Значительные различия
Программы на языке Java работают в своих собственных потоках; программы на языке JavaScript могут заблокировать ваш браузер.
Программы на языке Java являются изолированными объектами, выполняющимися в своих собственных потоках, находящихся в большом наборе выполняемых приложений в вашей системе. Программы на языке JavaScript, напротив, выполняются в браузере и соперничают друг с другом образом, не свойственным обычными приложениями для настольных компьютеров. В тот момент, когда программа на языке Java загружает файл, она ожидает окончания загрузки ресурса, после чего выполнение программы в обычном режиме продолжается. В том случае, когда программа является изолированным объектом, этот подход приемлем. Операционная система может отвечать на действия пользователя, так как она ответственна за планирование выполнения программных потоков и даже в том случае, когда программе требуется час для загрузки всех необходимых ей данных, вы также можете использовать свой компьютер. В случае веб-страницы процесс работы приложения значительно отличается. Если ваша "программа" на языке JavaScript ожидает загрузки ресурса, она заблокирует свой процесс до тех пор, пока ресурс не станет доступен. В том случае, если вы используете браузер, в котором для каждой вкладки создается отдельный процесс, ваша вкладка будет заблокирована, но браузер все равно можно будет использовать. Если вы используете браузер, не создающий таких процессов, он может перестать отвечать на запросы пользователя. Таким образом, независимо от назначения процесса, страница, на которой выполняется сценарий не сможет использоваться до момента загрузки ресурса, причем возможно, что ваш интерпретатор JavaScript полностью заблокирует браузер.
Такое поведение неприемлемо для современных веб-приложений, в которых ресурсы передаются асинхронно и страница должна работать в нормальном режиме в процессе фоновой загрузки ресурсов. Хотя это поведение и свойственно для традиционных веб-страниц, для веб-приложений оно становится реальной головной болью: как вы заставите код на языке JavaScript бездействовать в течение заданного промежутка времени в ожидании загрузки ресурса при условии того, что в рамках языка JavaScript не существует явного механизма для перевода приложения в режим ожидания? Хотя в рамках языка JavaScript также нет явного механизма для работы с потоками, он реализует модель событий и объект XMLHTTPRequest
, предназначенный для запроса произвольных (представленных не только в форматах XML или HTML) данных с использованием произвольных строк URL. Этот объект поддерживает несколько различных событий состояния и мы можем использовать его асинхронно для получения данных, причем в процессе браузер будет отвечать на запросы пользователя. Этот объект отлично подходит для программ, в которых осуществляется управление исходным кодом: вы просто останавливаете программу после осуществления запроса данных и возобновляете выполнение программы в момент, когда данные становятся доступны. Однако, это практически невозможно для кода, разработанного в соответствии с идеей синхронной загрузки ресурсов. Вставка "периодов ожидания" в программы, предназначенные для работы в режиме фиксированной частоты кадров не является приемлемой, поэтому нам придется прибегнуть к альтернативным подходам.
Все же в некоторых случаях мы решили применить операции синхронного ожидания. Например, при загрузке файла со строками используется синхронная версия объекта XMLHTTPRequest
, которая заблокирует выполнение сценариев страницы до тех пор, пока данные не станут доступны. В других случаях нам приходилось проявлять сообразительность. Для загрузки изображений, например, использовался встроенный механизм загрузки изображений браузера; мы создавали новый объект Image
с помощью JavaScript, устанавливали строку URL изображения в качестве значения его атрибута src
, после чего браузер выполнял всю остальную работу, уведомляя нас о том, что изображение доступно с помощью события onload
. Этот механизм даже не основывается на объекте XMLHTTPRequest
, он просто эксплуатирует возможности браузера.
Для упрощения работы в том случае, если вам заранее известно о том, какие изображения должны быть загружены, мы добавили поддержку директив предварительной загрузки и, таким образом, выполнение скетча не будет начато до тех пор, пока процесс предварительной загрузки изображений не будет завершен. Пользователь может задать любое количество изображений для предварительной загрузки с помощью блока комментариев в начале скетча; после этого библиотека Processing.js выполнит предварительную загрузку изображений. Событие onload
для каждого из изображений сообщит нам о том, что передача данных изображения закончена и оно подготовлено для вывода (изображение просто загружено, но пока не декодировано в массив пикселей), после чего мы можем загрузить данные в соответствующий объект Processing с именем PImage
с указанием корректных значений (width
(ширина), height
(высота), данные пикселей, и.т.д.) и удалить изображение из списка предварительной загрузки. В тот момент, когда список становится пустым, начинается выполнение скетча и используемые в ходе его выполнения изображения могут использоваться без необходимости ожидания их загрузки.
/* @pjs preload="./worldmap.jpg"; */ PImage img; void setup() { size(640,480); noLoop(); img = loadImage("worldmap.jpg"); } void draw() { image(img,0,0); }
Для других случаев нам пришлось разработать более запутанные системы "ожидания доставки ресурсов". Шрифты, в отличие от изображений, не имеют соответствующих встроенных в браузер механизмов загрузки (или, по крайней мере, системы, настолько же функциональной, насколько система загрузки изображений). Хотя загрузка шрифтов и может быть осуществлена с помощью правила CSS @font-face
, причем она будет осуществляться силами браузера, не существует событий JavaScript для уведомления об окончании загрузки шрифта. Мы наблюдаем медленный процесс добавления в браузеры функций, предназначенных для генерации событий JavaScript, указывающих на завершение процесса загрузки шрифтов, но эти события генерируются "слишком рано", так как браузеру после загрузки шрифта может потребоваться еще от нескольких до нескольких сотен миллисекунд для разбора шрифта перед его использованием на странице. Следовательно, использование этих событий может привести либо к невозможности применения шрифта, либо к применению отличающегося от указанного шрифта в том случае, когда указан шрифт, который должен использоваться в случае ошибки. Вместо использования этих событий мы включили в комплект поставки библиотеки замечательный шрифт формата TrueType, содержащий единственную букву "A" с чрезвычайно малыми метриками и передали браузеру команду для загрузки этого шрифта с помощью правила @font-face
со строкой URI, содержащей байтовое представление шрифта в форме строки в формате BASE64. Этот шрифт настолько малого размера, что мы можем быть уверены в том, что он будет доступен немедленно. Для любой другой инструкции загрузки шрифта мы сравниваем метрики текста для желаемого и встроенного шрифтов. Для скрытого тэга <div>
установлен атрибут использования желаемого шрифта, при этом в случае ошибки используется встроенный шрифт. В то время, как текст в рамках этого тэга <div>
имеет чрезвычайно малые размеры, нам известно, что желаемый шрифт пока не доступен, поэтому мы просто проверяем размеры шрифта через заданные интервалы времени до того момента, как метрики шрифта станут разумными.
Далее: Язык Java использует строгую типизацию; JavaScript не использует ее