Pull to refresh

HTML5: Web Workers и AJAX

HTML *
Все прочнее в среду разработчиков входит HTML5. Важным его достоинством является наличие такой технологии, как web workers, которая позволяет в некоторой степени обеспечить, если не мультипоточность, то ее подобие при выполнении скриптов.

Суть технологии проста — в отдельные файлы выносятся функции, обеспечивающие функционирование AJAX, либо функции обрабатывающие большие массивы информации, которые во время работы уменьшают скорость построения страницы. Таких файлов может быть столько сколько нужно. При выполнении скрипта на стороне браузера создается специальный объект Worker, который и отвечает за вызов необходимых функций. Многие современные браузеры поддерживают данную технологию.

Теперь о том, как использовать работников.
Проверим поддержку объекта браузером:


if (!!window.Worker)
{
//технология поддерживается
}

Создается объект очень просто:

var worker = new Worker(имя файла с исполняемой функцией);

созданный нами объект обладает следующими методами:

postMessage(); //ключевой метод, инициализирующий обмен данными
onmessage(); //метод, исполняемый при поступлении ответа от вызванного работника.
onerror(); //метод, вызываемый при возникновении ошибок

Теперь сделаем сам файлик worker`а и назовем его worker.js. Внутрь поместим следующий код:

onmessage = function(ev)
{
var answ = ev.data;
};

Объект и файл у нас есть, теперь остается только его вызвать делается это так:

var worker = new Worker('worker.js');
worker.postMessage('Hello World');

Переменная ev будет содержать объект, в свойстве data, которого будет находится то, что мы передадим в функцию, в нашем случае строка 'Hello World'. Передавать можно что угодно, в том числе сложные объекты. Теперь сделаем, чтобы наш работник, что-нибудь нам вернул. Для этого в код работника последней строкой допишем следующее:

postMessage(answ);

А в основном скрипте определим метод, который будет вызван при поступлении сообщения от работника:

worker.onmessage = function (event)
{
alert(event.data);
};


UPD:
Дополнение от demark:

Ещё надо добавить, что HTML5 позволяет создать Web Worker и без внешнего файла с помощью

blobBuilder'а:

var worker = new Worker(
    window.URL.createObjectURL(
        new BlobBuilder().append(
            "onmessage = function(e) { postMessage('hello habrahabr'); }"
        ).getBlob()
    )
);

worker.postMessage();


Вот собственно и все! Вернуть работник нам также сможет любые данные. Стоит помнить о нескольких важных моментах, таких как:
1. Работник не может получить доступ к DOM ни в каком виде. Ни одна функция работающая с DOM получающая данные о его состоянии или модифицирующая его, внутри работника недоступна. В том числе alert().
2. Работник имеет доступ к:
2.1 navigator
2.2 объект location (только чтение)
2.3 метод importScripts() (для доступа к файлам сценариев в том же домене)
2.4 объекты JavaScript, такие как Object, Array, Date, Math, String
2.5 XMLHttpRequest
2.6 методы setTimeout() и setInterval()

Так в чем же собственно мультипоточность? А в том, что объектов работников может быть сколько угодно и все они могут работать одновременно, при этом формирование страницы не будет остановлено, в ожидании пока отработает, какая-либо функция.

А теперь о важном, о том, чего нет ни в одной статье про работников: для того чтобы получить адекватный ответ при использовании технологии AJAX, необходимо отсылать СИНХРОННЫЕ запросы методом POST. Причина проста: при асинхронном запросе воркер заканчивает работу не дожидаясь ответа сервера. Логического объяснения почему не шлется методом GET я так и не нашел. Беспокоиться о том, что все встанет, не стоит, так как работник выполняется в отдельном потоке, основной скрипт будет работать дальше без остановок.

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

Приведу свою реализацию данного подхода:
1.
объект отвечающий за связь с сервером:

function  AJAXprov()
{
  var xmlhttp;
  var answServ;
  
  this.provXmlHttp = function()
  {
	  var xmlhttp;
    try 
    {
		  xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
	  } 
    catch (e) 
    {
		  try 
      {
  		  xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
  		} 
      catch (E) 
      {
		    xmlhttp = false;
		  }
	  }
	  if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
    {
		  xmlhttp = new XMLHttpRequest();
	  }
    return xmlhttp;
  }

//первый - режим работы false/true (синх/асинх), 
//второй - тип запроса (POST/GET)
//третий - параметры для запроса (адрес, по которому нужно отправить запрос, 
//четвертый - необязательный аргумент, содержащий параметры для POST запроса
  this.sendAnsServ = function (modeWork, typeSend, adr, param, id, cb)
  {
    if(typeSend == 'G')
    {
      adr = adr + '?' + param;
      //alert(adr);
      httpP.open('GET', adr, modeWork);
  	 	httpP.setRequestHeader('Cache-Control', 'no-cache, must-revalidate');
  		httpP.onreadystatechange = function()
  		{
        if (httpP.readyState == 4) 
    		{
          if(httpP.status == 200) 
          {
            if(cb)
            {
              return httpP.responseText;
            }
          }
        }
      }
      httpP.send(null);
    }
    if(typeSend == 'P')
    {
      httpP.open('POST', adr, modeWork);
      httpP.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    	httpP.setRequestHeader('Cache-Control', 'post-check=0,pre-check=0, false');
    	httpP.setRequestHeader('Cache-Control',  'max-age=0, false');
    	httpP.setRequestHeader('Pragma', 'no-cache');
    	httpP.setRequestHeader('Cache-Control', 'no-cache, must-revalidate');
    	httpP.send(param);
      if(httpP.status == 200)
      {
        if(cb)
        {
          return httpP.responseText;
        }
      }
    }
  }
}

2.
Воркер

onmessage = function (obj)
{
  importScripts("/JS/classes/AjaxClass.js ");
  var ajObj = new AJAXprov();
  httpP = ajObj.provXmlHttp();
  obj = obj.data;
  answ = ajObj.sendAnsServ(objEx.mode, objEx.type, objEx.adress, objEx.parametrs, objEx.ID);

  postMessage(answ);
}

вызов:

function crWorkerAjax(param, id, cb)
{

  var workerAjax = new Worker("/JS/workers/ajaxWorker.js");
  var objEx = 
    {
      mode:false,
      type:'P',
      adress:'/router.php',
      parametrs:param,
      ID:id,
    };


  workerAjax.onmessage = function (obj)
  {
    var res = eval(obj['data']);
    cb.call(this, res, id);
  }
  workerAjax.onerror = function(err)
  {
    alert(err.message);
  }
  workerAjax.postMessage(objEx);
  
}


UPD2:
По настойчивым просьбам yui_room9 привожу его альтернативный вариант работающий в браузерах на webkit

Для основного файла:

  if (!!window.Worker){
    var worker = new Worker('worker.js');
    worker.postMessage('Hellow World');
    worker.onmessage = function (e){
      alert(e.data);
    };
  }


 Для worker:

onmessage = function(e){
  transport = new XMLHttpRequest();
  transport.open('GET', 'data.txt', true);
  transport.onreadystatechange = function(){
    if(transport.readyState == 4){
      postMessage(transport.response);
    } 
  };
  transport.send();
};


Ну а в data.txt пихаем просто текст на тест, я пихал «testing123»

Всех, кто дочитал, благодарю за внимание.
Tags:
Hubs:
Total votes 49: ↑42 and ↓7 +35
Views 49K
Comments Comments 56