Скачать статью
Скачать Using Web Workers to Improve the Performance of Metro HTML5-JavaScript* Apps [Eng., PDF 591KB]
Цель
В этой статье описывается метод использование рабочих веб-процессов в приложениях HTML5 b JavaScript* стиля Metro. Мы рассказываем, что такое рабочие веб-процессы, каковы их преимущества и способы использования. В качестве демонстрации используется образец приложения. Основное внимание уделяется двум областям, где рабочие веб-процессы наиболее эффективны: сложные вычисления и фоновый обмен данными с серверами или загрузка содержимого в фоновом режиме.
Описание демонстрационного приложения
Приложения HTML5/JavaScript изначально рассчитаны на однопоточную работу. Нужно выделять достаточные промежутки времени между вычислениями, чтобы HTML DOM успевала обновлять элементы интерфейса и обрабатывать различные события. Без этих неблокирующих интервалов нужной длительности пользовательский интерфейс приложения и его часть, отвечающая за взаимодействие с пользователем, либо перестают работать вовсе, либо работают крайне медленно. Пользователи в таких случаях обычно считают, что приложение «зависло» или произошел сбой.
Для устранения этих проблем рекомендуется использовать методы асинхронного программирования приложений стиля Metro. Дополнительные сведения об асинхронном программировании в JavaScript смотрите здесь:
http://msdn.microsoft.com/en-US/library/windows/apps/hh700330
Методы асинхронного программирования помогут устранить проблемы взаимодействия приложения с пользователем, вызванные однопоточной обработкой, но приложения все равно могут работать недостаточно быстро. Кроме того, возможны случаи, когда приложение должно непрерывно что-то вычислять, отслеживать или обрабатывать в фоновом режиме. В таких случаях однопоточная обработка существенно снижает производительность.
Для решения описанных проблем были созданы рабочие веб-процессы (далее просто — «веб-процессы»). Они дают возможность создавать дополнительные потоки по мере необходимости. Веб-процессы не имеют доступа к DOM, они применяются только для действий, не связанных с DOM. Веб-процессы могут быть полезны в случаях, когда приложениям требуется выполнить сложные расчеты, которые могли бы заблокировать всю остальную работу, а также в случаях, когда приложениям нужно постоянно обмениваться данными с сервером или загружать данные в фоновом режиме.
Для демонстрации мы используем пример приложения под названием «Feed Analyzer». Смысл этого приложения состоит в том, чтобы загружать из Интернета несколько RSS-каналов, проводить анализ каналов по нескольким ключевым словам, определять число вхождений и разделять по категориям публикации по всем каналам на основе ключевых слов. Приложение может работать и с использованием веб-процессов, и без них. Пользователи могут выбрать количество веб-процессов, от 0 до 4. Если пользователь выберет 0 веб-процессов, они не будут использоваться. Графический интерфейс приложения приведен ниже:
Пользователи могут сравнивать производительность по счетчику затраченного времени, находящемуся в левом верхнем углу интерфейса приложения. При выборе любого ключевого слова все публикации с таким ключевым словом будут отфильтрованы и показаны в разделе «Feed Posts». Также пользователи могут щелкнуть «Show All», чтобы показать все публикации. При нажатии на кнопку «Start Feed Analysis» будет запущен (или начат заново) анализ каналов с указанным количеством веб-процессов (если указать 0, веб-процессы не будут использоваться). В приложении также будут показаны сообщения журнала каждого веб-процесса (или главного потока, если веб-процессы не используются). Пользователь может щелкнуть любую публикацию в канале, чтобы просмотреть полный текст статьи во всплывающем окне, как показано ниже.
Рабочие веб-процессы
Рабочие веб-процессы позволяют HTML-приложениям создавать дополнительные потоки для параллельной работы. Веб-процессы реализуют возможность многопоточного программирования, но не используют общие данные, благодаря чему модель программирования упрощается. Обмен всеми данными между веб-процессами и главным потоком осуществляется путем передачи сообщений (http://www.w3.org/TR/webmessaging/). Веб-процессы требуют достаточно много ресурсов, а создание нового потока занимает не так мало времени. В зависимости от требований приложения можно создавать дополнительные потоки при инициализации самого приложения, и затем использовать их, вместо того, чтобы создавать и закрывать их по требованию.
Стандарт веб-процессов (http://www.w3.org/TR/workers/) определяет два типа веб-процессов: выделенные и общие. Выделенные обладают лишь одним подключением и могут обмениваться данными только с родительским потоком (одна страница), а общие веб-процессы могут обладать множеством подключений к любым сценариям из одного источника. Приложения стиля Metro поддерживают только выделенные веб-процессы, поэтому в данной статье мы рассматриваем только этот тип.
Веб-процессы не имеют доступа к HTML DOM. Они имеют доступ к некоторым API, таким как XHR, расположению и навигатору, а также к стандартной библиотеке JavaScript. Дополнительные сведения о различиях между веб-страницей и веб-процессом см. в этой публикации в блоге:
Веб-процессы обмениваются данными с главным (родительским) потоком с помощью метода postMessage
и события message
(обработчик событий onmessage). Подробное описание методов, свойств и событий веб-процессов см. по адресу:
http://msdn.microsoft.com/en-us/library/windows/apps/hh767434.aspx
Веб-процессы также могут импортировать другие сценарии с помощью вызова функции importScripts
. Это дает возможность разделять логику приложения на несколько сценариев, а затем импортировать общие функции и элементы логики в веб-процессы по мере необходимости. Ниже приведет фрагмент кода веб-процесса из нашего демонстрационного приложения.
importScripts("//Microsoft.WinJS.1.0.RC/js/base.js", "/js/feedcommon.js");
Использование веб-процессов в приложениях стиля Metro
Веб-процессы в приложениях стиля Metro имеют доступ ко встроенному API платформы Metro. Они получают доступ к API Windows* WinRT таким же образом, как и главный поток приложения, с такими же ограничениями и правилами безопасности.
Создание нового веб-процесса осуществляется без всяких затруднений. В Visual Studio* предусмотрен шаблон веб-процесса по умолчанию, при помощи которого можно создать выделенный веб-процесс, как показано ниже.
Этот шаблон создаст новый файл JavaScript с заглушкой обработчика событий onmessage
и добавит этот файл в папку проекта нашего приложения. Необходимо записать имя файла этого сценария (в нашем примере — feedworker.js), поскольку нужно указать его в вызове функции конструктора веб-процесса.
Можно указать весь код рабочего веб-процесса в файле исходного кода этого процесса или импортировать код из других сценариев. В нашем приложении мы реализуем основные функции в одном файле JavaScript под названием feedcommon.js. Можно импортировать этот файл в веб-процесс с помощью функции importScripts
, а также можно импортировать его в код, не использующий веб-процессы, обычным способом импорта страниц (тег script).
Ниже приведен полный исходный код веб-процесса (feedworker.js).
importScripts("//Microsoft.WinJS.1.0.RC/js/base.js", "/js/feedcommon.js"); onmessage = function (ev) { feedAnalyzeAsync(ev.data.url, ev.data.tags, ev.data.source) .then(function (ev) { postMessage("end"); }, function (ev) { postMessage(ev); postMessage("end"); }, function (ev) { postMessage(ev); }); }
Мы импортируем и базовый API WinJS, и общую функциональность нашего приложения (feedcommon.js). Обратите внимание, что обработчик событий onmessage просто вызывает общую функцию, а затем передает результаты главному потоку с помощью функции postMessage
. Областью этого сценария по умолчанию является глобальный сценарий веб-процесса («self»), поскольку мы можем напрямую вызывать методы типа postMessage
и onmessage
.
Главная функция приложения — получение нескольких каналов (фоновая загрузка) и поиск и анализ информации (сложные блокирующие вычисления) — реализуется в виде одной функции feedAnalyzeAsync в общем сценарии (feedcommon.js). Поскольку общий сценарий использует API WinRT и WinJS, веб-процессы в нашем приложении будут выполнять оба этих API (для демонстрации доступа веб-процессов к WinRT и WinJS). Также мы имеем возможность сравнивать производительность веб-процессов, поскольку они используют одинаковый путь к базовому коду.
Создание новых экземпляров веб-процессов из главного потока приложения осуществляется без особых затруднений. Достаточно указать имя файла сценария веб-процесса в конструкторе рабочих процессов, как показано ниже ("new Worker").
for (var wwi = 0; wwi < parseInt(id("wwbtn").max) ; wwi++) { wworkers[wwi] = new Worker('/js/feedworker.js'); wworkers[wwi].onmessage = function (ev) { if (ev.data === 'end') enablefabtn(); else facallback(ev.data); } }
Обратите внимание, что мы создаем сразу нескольких рабочих процессов и настраиваем обработчик событий (onmessage) для каждого из них. Веб-процессы требуют достаточно много ресурсов, а создание экземпляра потока занимает определенное время. В нашем приложении мы создаем новые процессы при запуске приложения, на этапе инициализации.
После создания рабочий процесс может получать сообщения с помощью обработчика событий onmessage
и обмениваться данными с главным потоком с помощью вызова метода postMessage
. Главный процесс может обмениваться данными с рабочими процессами с помощью вызова метода postMessage
и получать сообщения с помощью обработчика событий onmessage
для объектов рабочих процессов.
Данные, передаваемые между главным потоком и рабочим процессом с помощью метода postMessage
, могут быть любого типа, если этот тип поддерживается стандартом передачи сообщений (http://www.w3.org/TR/webmessaging). В нашем приложении используется повсеместно поддерживаемый формат JSON.
Улучшение производительности при использовании веб-процессов
Рабочие веб-процессы могут быть полезны в самых разных случаях. Например, для обработки длительных задач отслеживания в фоновом режиме, для сложных вычислений, для обработки логики, не относящейся к пользовательскому интерфейсу (в этом случае логика интерфейса DOM обрабатывается главным потоком), или даже для вызова определенных действий в играх. Во всех этих случаях применение веб-процессов приводит к повышению скорости работы или к более быстрому отклику.
В нашем приложении FeedAnalyzer используется два основных сценария: фоновая загрузка и сложные вычисления. Для объективного сравнения производительности с веб-процессами и без них мы используем одинаковый путь к базовому коду в обоих случаях. В обоих сценариях мы используем асинхронный метод обработки (WinJS).
Приведенный ниже фрагмент кода взят из распространенной функции фоновой загрузки (код приведен не полностью):
function feedAnalyzeAsync(url, tags, source) { return new WinJS.Promise(function (completed, failed, progress) { try { var uri = new Windows.Foundation.Uri(url); var synd = new Windows.Web.Syndication.SyndicationClient(); synd.bypassCacheOnRetrieve = true; progress({ 'id': source, 'type': 'log', 'data': "Downloading Feed..." }); synd.retrieveFeedAsync(uri).done(function (f) { var ftitle = f.title ? f.title.text : "(no title)" progress({ 'id': source, 'type': 'log', 'data': 'Download Complete! Analyzing feed "' + ftitle + '"' });
Мы помещаем общую функцию внутри блока WinJS, поскольку она может выполняться в течение длительного времени. Мы используем API синдикации WinRT для асинхронной фоновой загрузки каналов. После полной загрузки канала мы анализируем все публикации в этом канале на вхождение ключевых слов и имитируем сложные вычисления путем длительных блокировок. Ниже приведен фрагмент кода для этого сценария использования (код приведен не полностью).
// some heavy computation - search for all keywords in each post var tagfound; tagfound = 0; tags.forEach(function (tag) { var m = content.match(new RegExp(tag, 'gi')); if (m) { result['tags'][tag] = m.length; logmsg += ' (' + m.length + "" + tag + ')'; tagfound = 1; } }); if (tagfound === 0) logmsg += ' ( none )'; // simulate even more heavy computation - block, then delay processing using promise timeout var time = new Date(); while ((new Date() - time) < 100); WinJS.Promise.timeout(200 * (i+1)).then(function (source, logmsg, result, test) { return function () { progress({ 'id': source, 'type': 'log', 'data': logmsg + ' in "' + result['title'] + '"' });
Когда пользователь нажимает кнопку «Start Feed Analysis», приложение начинает загружать несколько каналов и в фоновом режиме анализирует все публикации в каждом канале. Если веб-процесс не используется, приложение использует асинхронный режим работы, чтобы можно было обновлять пользовательский интерфейс после анализа каждой публикации. Приложение выводит затраченное время для обоих случаев.
Ниже приведен снимок экрана для работы приложения без веб-процессов.
В нашей тестовой системе результат составил 34 743 миллисекунды. В вашей тестовой системе результат может отличаться.
При использовании веб-процессов запускается указанное пользователем число веб-процессов, а каналы распределяются между ними по принципу карусели. Каждый рабочий процесс будет начинать загрузку канала и анализировать все публикации в каждом канале (как в сценарии без использования веб-процессов). По завершении работы всех веб-процессов выводится затраченное время. Ниже приведен снимок экрана для работы приложения при использовании 4 веб-процессов.
Как видно на приведенном выше рисунке, при использовании веб-процессов сценарий был выполнен значительно быстрее — за 27 300 мс. Обратите внимание, что все результаты получены на нашей тестовой системе. Результаты на других компьютерах могут отличаться. В целом, при использовании веб-процессов в системах с многопоточными ЦП достигается существенное повышение производительности.
Итоги
Приложения HTML5/JavaScript выполняются в один поток и могут «зависать» или работать крайне медленно, если такие приложения содержат код, блокирующий выполнение в течение длительного времени. Веб-процессы позволяют решить эти проблемы. Мы описываем веб-процессы, их преимущества и способы использования. Мы также показываем использование веб-процессов в приложениях HTML5/JavaScript стиля Metro и приводим показатели производительности приложения с использорванием веб-процессов и без них. В продемонстрированы две основные области использования, в которых веб-процессы обеспечивают существенное повышение производительности: сложные вычисления и фоновая загрузка и анализ данных.
*Другие наименования и торговые марки могут быть собственностью третьих лиц.
**Пример исходного кода распространяется на условиях соглашения Intel Sample Source Code License Agreement