Статьи

Более глубокий взгляд на HTML 5 веб-работников

В этой статье мы более подробно рассмотрим некоторые аспекты веб-работников HTML 5, чем в предыдущей статье « Введение в веб-работников HTML 5 ».


Если вы новичок в HTML 5 Web Workers, то я рекомендую вам прочитать мой предыдущий пост, в котором подробно описаны различные типы веб-работников, которые доступны, как создавать рабочие объекты и как общаться между работником и его создателем.

Несколько общих работников

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

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

Ниже представлен простейший способ создания потока SharedWorker, который могут использовать несколько окон из одного источника:

    // Window 1
    var aSharedWorker = new SharedWorker("SharedWorker.js");
    // Window 2
    var aSharedWorker = new SharedWorker("SharedWorker.js");

Объект SharedWorker принимает необязательный второй параметр в конструкторе, который служит именем работника.

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

    // Window 1
    var aSharedWorker = new SharedWorker("SharedWorker.js", "SharedWorkerName");
    // Window 2
    var aSharedWorker = new SharedWorker("SharedWorker.js", "SharedWorkerName");
Большую часть времени наличие одного общего работника даст необходимую функциональность. Если у вас просто есть желание добавить больше параллельной обработки, совместно используемый работник всегда может создавать своих собственных веб-работников (при необходимости выделенные веб-работники могут также создавать дополнительные рабочие потоки).

Что, если вы столкнетесь со сценарием, в котором вам нужно несколько окон для совместного использования нескольких рабочих, а не только одного?

Вот где вступает в действие второй параметр конструктора SharedWorker.

Вы можете создать несколько разных потоков SharedWorker, указав разные имена при создании рабочих объектов.
Ниже приведен пример двух окон, каждое из которых имеет два рабочих потока: «Рабочий1» и «Рабочий2»:


    // Window 1 - Shared Worker 1 & 2
    var aSharedWorker1 = new SharedWorker("SharedWorker.js", "Worker1");

    var aSharedWorker2 = new SharedWorker("SharedWorker.js", "Worker2");
    // Window 2 - Shared Worker 1 & 2
    var aSharedWorker1 = new SharedWorker("SharedWorker.js", "Worker1");

    var aSharedWorker2 = new SharedWorker("SharedWorker.js", "Worker2");


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

Если вы дадите одному работнику имя Worker1, а другому работнику — имя worker1, они могут выглядеть одинаково, но разница в регистре с ‘ w ‘ приводит к созданию двух рабочих потоков, а не просто созданию и повторному использованию одного рабочего потока.

Объем общих работников

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

Общий работник останется активным, пока к нему подключено одно окно. Например:

Окно 1 создает общего работника, и Окно 2 подключается к нему.

Позже Окно 1 закрывается, но, поскольку Окно 2 все еще подключено, общий рабочий поток остается, даже если Окно 2 изначально не создавало его.
Импорт JavaScript в рабочий поток

Большинство современных веб-разработок теперь включают в себя несколько файлов JavaScript и, во многих случаях, библиотеки / фреймворки JavaScript.

К счастью, рабочие потоки имеют доступ к глобальной функции importScripts, которая позволяет вам перетаскивать необходимые файлы JavaScript в область рабочего потока.

Функция importScripts позволяет вам указать один или несколько файлов JavaScript для импорта.

Если указано несколько файлов, они загружаются параллельно, но будут загружаться и обрабатываться синхронно в указанном порядке.

Кроме того, функция importScripts не возвращает, пока указанные файлы JavaScript не будут загружены и обработаны.

Ниже приведены некоторые примеры использования функции importScripts:

    // Import just one file
    importScripts("file.js");
    // Import multiple files with one call (careful
    // of their order if they have global variables
    // that depend on other js files being included
    // in the request...the JS files will be
    // downloaded in parallel but loaded and
    // processed synchronously based on their order
    // here).
    // Note: I stopped at 3 files to import but you
    // can include any amount
    importScripts("file1.js", "file2.js", "file3.js");

Технически, спецификация W3C позволяет использовать функцию importScripts без параметров, но это не имеет никакого эффекта.

Прикрепление к «onmessage» внутри общего работника

В моем предыдущем посте я показал следующий метод присоединения к событию onmessage общего рабочего (в ветке):

    onconnect = function (evt)
    {
    var port = evt.ports[0];
    port.onmessage = function (e) { OnControllerMessage(e, port); }
    }
    function OnControllerMessage(e, port)
    {
    var sReturnMessage = ("Hello from the sharedworker thread!...This is what you sent my way: " + e.data);

    port.postMessage(sReturnMessage);
    }

К сожалению, приведенный выше пример был выполнен путем создания замыкания, и я не фанат использования замыканий.

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

В следующем примере демонстрируется подключение к событию onmessage и отправка данных обратно вызывающей стороне через свойство target события:

    onconnect = function (evt)
    {
    evt.ports[0].onmessage = OnControllerMessage;
    }
    function OnControllerMessage(e)
    {
    var sReturnMessage = ("Hello from the sharedworker thread!...This is what you sent my way: " + e.data);

    e.target.postMessage(sReturnMessage);
    }


Таймауты

Рабочие потоки поддерживают тайм-ауты (setTimeout, clearTimeout, setInterval и clearInterval), что полезно, если вы хотите обрабатывать информацию только через заданные интервалы, а не постоянно обрабатывать.

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

Вы можете настроить таймер для проверки сайта каждые пять минут или около того, и, если доступна новая информация, передать ее на уровень пользовательского интерфейса, чтобы ее можно было включить в информацию отображения.

Объект XMLHttpRequest

Объект XMLHttpRequest может использоваться в рабочем потоке и может использоваться синхронно или асинхронно. Это полностью зависит от вас, если вы используете его асинхронно, так как рабочий поток уже отделен от потока пользовательского интерфейса, поэтому ожидание синхронного возврата не повредит отзывчивости пользовательского интерфейса.

Что следует знать об использовании объекта XMLHttpRequest из рабочего потока, так это то, что свойство responseXML всегда будет нулевым.

Специфичный для Gecko атрибут channel в экземпляре XMLHttpRequest также будет иметь значение null, если объект XMLHttpRequest используется в рабочем потоке.

Возвращаемые данные из XMLHttpRequest, находящиеся в рабочем потоке, всегда будут находиться в свойстве responseText, как показано в следующем примере:

    var xhrRequest = new XMLHttpRequest();
    xhrRequest.open("GET", sURL, false);
    xhrRequest.send();
    return xhrRequest.responseText;

Обработка ошибок

Необработанные исключения могут быть перехвачены в рабочем потоке путем присоединения функции к глобальному событию onerror.

Здесь есть некоторые отличия браузера:

  • Chrome 5 и Safari 5 просто передают ошибку в виде строки в обработчик ошибок в потоке
  • Firefox 3.6.8 и 4.0 beta 2 передают объект ErrorEvent обработчику ошибок в потоке



Атрибуты объекта ErrorEvent:

  • message — удобочитаемое сообщение об ошибке
  • filename — имя файла скрипта, в котором произошла ошибка
  • lineno — номер строки файла скрипта, в котором произошла ошибка

Ниже приведен пример присоединения к событию onerror выделенного рабочего потока (этот пример также будет работать для совместно используемых рабочих, за исключением того, что для совместно используемых рабочих необходимо вызывать postMessage через порт):

    // Attach to the global error handler of the
    // thread
    onerror = OnErrorHandler;
    function OnErrorHandler(e)
    {
    // In Chrome 5/Safari 5, 'e' is a string for
    // both dedicated and shared workers within
    // the thread
    if (typeof (e) == "string")
    {
    postMessage("Error Message: " + e);
    }
    else // Dedicated worker in Firefox...(Firefox
    // does not yet support shared workers)
    {
    postMessage("Error Message: " + e.message + " File Name: " + e.filename + " Line Number: " + e.lineno);
    }
    }

    // to test the error handler, throw an error
    throw "This is a test error";

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

Все браузеры (Chrome 5, Safari 5, Firefox 3.6.8 / 4.0 beta 2) реализуют событие ошибки выделенного рабочего экземпляра одинаково, передавая объект ErrorEvent.

Однако, когда дело доходит до общих работников, я не смог получить экземпляр объекта общего рабочего, чтобы вызвать событие onerror в Chrome 5 или Safari 5. Из моего исследования выяснилось, что для общих работников событие onerror будет вызвано только для общего рабочего экземпляра, если во время создания рабочего потока произошла сетевая ошибка.

Ниже приведен пример подключения к событию onerror выделенного рабочего экземпляра (вы настраиваете общий рабочий точно так же):

    var aWorker = new Worker("DedicatedWorker.js");

    aWorker.onerror = OnErrorMsg;
    function OnErrorMsg(e)
    {
    alert("Error Message: " + e.message + " File Name: " + e.filename + " Line Number: " + e.lineno);
    }
Закрытие рабочего потока

Создатель работника может закрыть поток, вызвав ‘terminate’ на экземпляре работника, как в следующем примере:

    var aDedicatedWorker = new Worker("DedicatedWorker.js");

    ...

    // We're done with the tread so let the
    // browser release the system resources
    aDedicatedWorker.terminate();

Альтернатива использованию метода terminate на экземпляре работника заключается в том, что поток может закрывать себя, как в следующем примере:

    // JavaScript of the thread itself

    ...

    close();

В заключение

Between my previous post ‘An Introduction to HTML 5 Web Workers‘ and this one, I hope I have been able to help you in your understanding of web workers so that you can make use of all the possibilities this exciting new technology brings to web development!

If you would like more information on the HTML 5 Web Workers specification you can click on the following links:
http://www.whatwg.org/specs/web-workers/current-work/
http://www.w3.org/TR/workers/

 

Source: http://cggallant.blogspot.com/2010/08/deeper-look-at-html-5-web-workers.html