Скрещиваем WebWorker и XMLHttpRequest

WebWorker+XMLHttpRequest

HTML5 уже никого не удивляет, но у многих новичков возникает много вопросов. Особенно вопросы связаные с параллельными потоками, а именно с WebWorker. Дальнейшее повествование требует знания JS и HTML — я не буду разжевывать основы html и js.
Сегодня мы рассмотрим как обернуть в WebWorker обычный XMLHttpRequest.
Первым, естественно, шагом будет создание обычной функции.
function req0() {}

Теперь необходимо создать работника:
Th0=new Worker('Th0.js')

Для работы с WebWorker нам понадобиться знать всего лишь 3 команды: передача, прием, удаление.
Для основной функции:
onmessage -прием сообщения или, грубо говоря, это то, что будет делать скрипт после завершения работы WebWorker.
postMessage — отправка сообщения или запуск работника. Здесь можно передать данные в поток.
terminate — завершение Работника.
Для самого Работника в файле скрипта:
onmessage — грубо говоря, это то, что будет выполнять Работник при его запуске
postMessage — это то, что Работник отправит в главный скрипт, то есть результаты выполненной работы
Теперь надо создать файл Th0.js — в нем и будет находится основной код Работника. Стоит заметить, что WebWorker не работает локально, как например, обычный html документ. Для WebWorker необходим веб-сервер.
Открываем Th0.js и пишем:
onmessage=function(event) {
nameRQ=event.data;}

Теперь у нас есть функция, которую будет выполнять Работник. Переменная nameRQ необходима для передачи функции запроса имени файла, в котором содержится запрашиваемая информация. Теперь надо написать XMLHttpRequest. Все знают как? Для большего понимания советую прочитать дополнительный материал про XMLHttpRequest-запросы. Я лишь вкратце расскажу.
Создаем запрос:
xhttp=new XMLHttpRequest()

Пишем простенький обработчик ответа сервера. Безусловно, можно написать хороший обработчик с распознаванием ошибок и реакцией на них, но сейчас не об этом.
	xhttp.onreadystatechange=function(){if (xhttp.readyState==4&&xhttp.status==200) {postMessage({goodReq0:xhttp.responseText})}};

if (xhttp.readyState==4&&xhttp.status==200) — обработчик ответа сервера. Если запрос успешно завершен, тогда выполнить следующую функцию:
{postMessage({goodReq0:xhttp.responseText})}};}

Теперь надо запустить сам запрос:
xhttp.open('POST','http://domen.dmn/req/'+nameRQ,true);
xhttp.send();

Запрос будет POST, асинхронным.
domen.dmn/req'+nameRQ — это путь к файлу на сервере. Соответственно, в папке req на сервере будет находится искомый файл с запрашиваемым именем — в данном случае имя характеризуется переменной nameRQ. Это позволит одним запросом обращаться к разным файлам, меняя переменную.
Итоговый код файла Th0.js
onmessage=function(event) {
	nameRQ=event.data; 	/* Имя запрашиваемого файла */
	xhttp=new XMLHttpRequest(); /* создаем XMLHttpRequest-запрос */
	xhttp.onreadystatechange=function(){if (xhttp.readyState==4&&xhttp.status==200) {postMessage({goodReq0:xhttp.responseText})}}; /* Обработчик запроса */
	xhttp.open('POST','http://domen.dmn/req/'+nameRQ,true); /* открываем запрос */
	xhttp.send(); /* и запускаем */
}

Возвращаемся к основной функции.
Пишем обработчик ответа Работника.
Th0.onmessage=function(event) {document.getElementById('DivRQ').innerHTML=event.data.goodReq0;}

document.getElementById('DivRQ').innerHTML — Вставляет полученный текст в Div с именем DivRQ (который заранее необходимо создать на страничке).
event.data.goodReq0 — переменная с данными, полученными от сервера.
«Убиваем» Работника:
Th0.terminate();

Теперь собственно пишем функцию запуска работника. В коде сначала идет onmessage затем postMessage..
Th0.postMessage(nameRQ)}

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

Итоговый файл:
function req0() {
	Th0=new Worker('Th0.js'); /* создаем Работника */
	Th0.onmessage=function(event) {document.getElementById('DivRQ').innerHTML=event.data.goodReq0;	/* обработчик  */		Th0.terminate();} /* завершаем WebWorker */		
	Th0.postMessage(nameRQ)} /* запускаем WebWorker */	

Вот и готов наш «параллельный запрос». Теперь их можно делать параллельно.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 12

    +5
    А зачем?
      0
      WebWorker необходим для распараллеливания, что в свою очередь позволяет задействовать большее количество ядер. Что Теоретически (когда частота каждого ядра невысока, а нагрузка различными скриптами высока) позволяет повысить скорость работы, задействуя несколько потоков. Да и разработчики WebWorker не зря же сделали поддержку запросов?
      P.S: пару раз просили помочь с подобным.
        +1
        Большее количество ядер для какой именно работы? XHR не cpu intensive операция.

        ps: у вас там `var` во всех примерах с кодом потерялись
        0
        XHR — да. Но помимо нее параллельно может идти множество других процессов. XHR не особо требователен к CPU, но ведь и в WebWorker его зачем-то добавили. А для простоты разработки могли бы и не добавлять — WebWorker итак имеет много ограничений, могли бы добавить и еще +1 ограничение.
        В любом случае, раз был интерес к этой теме со стороны различных людей, то зачем-то им это надо?

          +2
          то зачем-то им это надо


          Ну вот вы написали статью. Вам задали вопрос — зачем так? Почему не отправлять XHR из основного потока?

          Вы заметно усложнили код, сильно усложнив его поддержку + отладку. Что вы получили взамен?
          +1
          Было подобное: habrahabr.ru/post/132785/
          Хотя конечно любые полезные начинания — это хорошо.

          З.Ы. Подправьте своё написание кода, хотя бы для статьи, а то жуть просто.
            +1
            Как выше написали, работа с сетью в js не cpu intensive, специально выделять ее в воркеры не нужно. А поддержку xhr из webworkers сделали потому что это может кому-нибудь понадобиться. Например вы качаете много данных, а потом делаете много cpu intensive действий с ними. Проще будет написать всю эту логику в воркере, а не качать данные отдельно в основном потоке, чтобы потом отправить их в воркер.

            Так что делать как указано в статье не стоит. Сложно и незачем.
              0
              Первое, если стоит сложный обработчик запросов, то он как минимум будет выполнять фоном свою работу — если получил ошибку от сервера, обработал, если что заново послал запрос и так далее. Если запросов много это скажется, хоть и незначительно. Второе. Если в одном потоке сразу несколько запросов и их надо либо сравнивать, либо производить какие-либо действия, к примеру, математические. Тогда запрос становится весьма тяжелым. Например, запросом считывается параметры векторов, которые потом обрабатываются в этом же потоке. Или же делается 10 запросов, которые либо сравниваются, либо подставляются в различные формулы. То есть параллельный поток создается не для запроса, а для вычислений, а запрос уже как вторичная задача, дескать, подхватить нужные данные для расчета. В таком случае — расчет весьма затратный по времени, делать его в основном потоке уже неактуально, поэтому и запрос проще сделать в фоновом расчете — там же, где и расчет. В итоге, можно запустить несколько фоновых расчетов со своими запросами. Можно сделать и наоборот — запрос в основном потоке, и пересылать результаты запроса через postMessage в поток — но тогда это усложнение, так как проще сделать запрос в том месте, где нужны эти данные.

                0
                Предположим по результатам тех или иных расчетов должен произойти тот или иной запрос, после которого пойдут дальше расчеты. Если запрос будет сделан в основном потоке, то необходимы постоянные пересылки туда-сюда данных. Если запрос сделан в фоновом потоке, где и производится расчет, то никаких пересылок не надо.
                  –1
                  А разве XmlHttpRequest не работает сам по себе в отдельном потоке? А если его запустить из WebWorker-а, то будет ли он создавать отдельный поток для запроса или будет работать в том же? Если будет создавать отдельный, то не затратно ли это по два потока на запрос? Не эффективнее ли было бы посылать запрос из основного потока, а обрабатывать ответ в воркере?
                  0
                  Опять же. Если по результатам расчетов надо делать тот или иной запрос — зачем 100 раз делать пересылки из основного потока в ВебВоркер? А так, идет расчет в фоновом потоке, если нужно ему нужно подгрузить данные, он их сам и подгружает без излишних переселок.

                  Only users with full accounts can post comments. Log in, please.