Донецкий техникум промышленной автоматики

The Basics of Web Workers

  1. Проблема: паралельне виконання коду JavaScript
  2. Знайомство з об'єктами Web Worker: многопоточность в JavaScript
  3. Типи об'єктів Web Worker
  4. Початок роботи
  5. Взаємодія з об'єктами Worker шляхом обміну повідомленнями
  6. Серед об'єктів Worker
  7. Використання опцій для об'єктів Web Worker
  8. Завантаження зовнішніх скриптів
  9. Субоб'екти Worker
  10. Вбудовувані об'єкти Web Worker
  11. URL об'єктів Blob
  12. повний приклад
  13. Завантаження зовнішніх скриптів
  14. Обробка помилок
  15. Трохи про безпеку
  16. Рекомендації по використанню одного джерела
  17. приклади використання

Проблема: паралельне виконання коду JavaScript

Є кілька моментів, які дозволяють переносити цікаві JavaScript-програми на сторону клієнта (наприклад, для розвантаження серверів). До них відносяться сумісність браузерів, статична типізація, доступність і ефективність. На щастя, остання проблема швидко відходить в минуле, оскільки розробники браузерів постійно підвищують швидкість функціонування платформ JavaScript.

Однією з проблем, пов'язаних з JavaScript, залишається сама мова. JavaScript - це однопоточні середовище, в якому кілька скриптів не можуть виконуватися одночасно. Наприклад, уявімо сайт, на якому необхідно управляти подіями інтерфейсу користувача, запитувати і обробляти великі обсяги даних API і працювати з моделлю DOM. Дуже знайоме, чи не так? На жаль, всі ці дії не можуть виконуватися одночасно через обмеження в середовищі виконання JavaScript браузерів. Скрипти виконуються в межах одного потоку.

Розробники імітують паралельне виконання коду за допомогою таких засобів, як методи setTimeout () і setInterval (), технологія XMLHttpRequest, а також обробники подій. Всі ці функції виконуються асинхронно, але відсутність блокування не обов'язково означає паралельне виконання. Асинхронні події обробляються після повернення з поточного виконується скрипта. Приємним моментом є той факт, що в HTML5 є більш зручний спосіб реалізації паралельного виконання.

Знайомство з об'єктами Web Worker: многопоточность в JavaScript

Специфікація об'єктів Web Worker визначає API для створення фонових скриптів в веб-додатках. Ці об'єкти дозволяють запускати довготривалі скрипти для виконання завдань, що вимагають великого обсягу обчислень, не вдаючись до блокування інтерфейсу користувача або інших скриптів, керуючих взаємодією користувача з системою. Вони допомагають позбутися від обридлого діалогового вікна "Скрипт не відповідає", до якого всі вже встигли звикнути.

Стандартне діалогове вікно не відказали скрипта

Об'єкти Web Worker використовують потокову передачу повідомлень для реалізації паралельності. Вони ідеально підходять для поновлення інтерфейсу, забезпечення його ефективності та реалізації оперативної взаємодії з користувачами.

Типи об'єктів Web Worker

Слід зауважити, що в специфікації визначаються два типи об'єктів Web Worker: виділені і загальні . У цій статті розглядаються тільки виділені об'єкти Web Worker (ми будемо називати їх об'єктами Web Worker або об'єктами Worker).

Початок роботи

Об'єкти Web Worker запускаються в ізольованому потоці. З цієї причини виконується ними код повинен зберігатися в окремому файлі. Однак спочатку необхідно створити новий об'єкт Worker на головній сторінці. Конструктор приймає назву скрипта об'єкта:

var worker = new Worker ( 'task.js');

Якщо вказаний файл існує, браузер створить новий потік об'єкта Worker, що завантажується асинхронно. Об'єкт не запускається до повного завантаження і виконання файлу. Якщо шлях до об'єкта Worker повертає помилку 404, його виконання припиняється без повідомлень.

Після створення об'єкта Worker його можна запустити за допомогою методу postMessage (), як показано нижче.

worker.postMessage (); // Start the worker.

Взаємодія з об'єктами Worker шляхом обміну повідомленнями

Взаємодія об'єкта Worker з вихідної сторінкою здійснюється за допомогою моделі подій і методу postMessage (). Залежно від типу і версії браузера метод postMessage () приймає рядок або об'єкт JSON в якості єдиного аргументу. Новітні версії сучасних браузерів підтримують передачу об'єктів JSON.

Нижче наведено приклад використання рядка для передачі повідомлення Hello World об'єкту Worker в скрипті doWork.js. Об'єкт Worker просто повертає отримане повідомлення.

Основний скрипт:

var worker = new Worker ( 'doWork.js'); worker.addEventListener ( 'message', function (e) {console.log ( 'Worker said:', e.data);}, false); worker.postMessage ( 'Hello World'); // Send data to our worker.

doWork.js (об'єкт Worker)

self.addEventListener ( 'message', function (e) {self.postMessage (e.data);}, false);

Якщо метод postMessage () викликається на головній сторінці, об'єкт Worker обробляє повідомлення, визначаючи обробник onmessage для події message. Інформаційне наповнення повідомлення (в даному випадку - Hello World) є в об'єкті Event.data. Хоча цей конкретний приклад і не є цікавим, він демонструє, що метод postMessage () також служить для передачі даних назад в головний потік. Зручність використання

Повідомлення, що передаються між головною сторінкою та об'єктами Worker, копіюються, але загальний доступ до них не надається. Так, в наступному прикладі властивість msg повідомлення JSON є в обох місцях. Може здатися, що елемент передається безпосередньо об'єкту Worker, хоча дія виконується в виділеному просторі. Насправді при передачі в об'єкт Worker елемент перетворюється в послідовну форму, а зворотне перетворення потім відбувається в кінці шляху. Сторінка і об'єкт Worker не мають загального доступу до одного екземпляра елемента, і в результаті кожен раз при передачі створюється його дублікат. Більшість браузерів реалізують цю функцію шляхом автоматичної кодування і декодування значення в кінцевих точках маршруту, використовуючи для цього технологію JSON.

Нижче наведено більш складний приклад, в якому обмін повідомленнями здійснюється за допомогою об'єктів JSON.

Основний скрипт:

<Button onclick = "sayHI ()"> Say HI </ button> <button onclick = "unknownCmd ()"> Send unknown command </ button> <button onclick = "stop ()"> Stop worker </ button> < output id = "result"> </ output> <script> function sayHI () {worker.postMessage ({ 'cmd': 'start', 'msg': 'Hi'}); } Function stop () {// Calling worker.terminate () from this script would also stop the worker. worker.postMessage ({ 'cmd': 'stop', 'msg': 'Bye'}); } Function unknownCmd () {worker.postMessage ({ 'cmd': 'foobard', 'msg': '???'}); } Var worker = new Worker ( 'doWork2.js'); worker.addEventListener ( 'message', function (e) {document.getElementById ( 'result'). textContent = e.data;}, false); </ Script>

doWork2.js

self.addEventListener ( 'message', function (e) {var data = e.data; switch (data.cmd) {case 'start': self.postMessage ( 'WORKER STARTED:' + data.msg); break; case 'stop': self.postMessage ( 'WORKER STOPPED:' + data.msg + '. (buttons will no longer work)'); self.close (); // Terminates the worker. break; default: self.postMessage ( 'Unknown command:' + data.msg);};}, false);

Примітка. Припинити роботу об'єкта Worker можна двома способами: викликати метод worker.terminate () на головній сторінці або self.close () в самому об'єкті.

Приклад. Запустіть цей об'єкт Worker.

Привітатися Послати невідому команду Зупинити об'єкт Worker

Серед об'єктів Worker

Область дії об'єкта Worker

В контексті об'єкта Worker і self, і this відносяться до глобальної області дії об'єкта. Таким чином, зазначений вище приклад також можна записати в такий спосіб:

addEventListener ( 'message', function (e) {var data = e.data; switch (data.cmd) {case 'start': postMessage ( 'WORKER STARTED:' + data.msg); break; case 'stop': ...}, false);

Крім того, можна налаштувати безпосередньо обробник подій onmessage (хоча розробники JavaScript завжди пропонують використовувати addEventListener).

onmessage = function (e) {var data = e.data; ...};

Використання опцій для об'єктів Web Worker

У зв'язку зі своїм багатопотоковим характером об'єкти Web Worker мають доступ тільки до певного набору функцій JavaScript, зазначених нижче.

Об'єкти Web Worker не мають доступу до наступних можливостям і елементам.

  • Модель DOM (вона не орієнтована на багатопотокове виконання)
  • об'єкт window
  • об'єкт document
  • об'єкт parent

Завантаження зовнішніх скриптів

За допомогою функції importScripts () можна завантажувати в об'єкт Worker зовнішні файли скриптів і бібліотек. Цей метод приймає нуль або більше рядків, що представляють назви файлів для імпорту ресурсів.

У цьому прикладі в об'єкт завантажуються файли script1.js і script2.js.

worker.js:

importScripts ( 'script1.js'); importScripts ( 'script2.js');

Це також можна записати в вигляді єдиного оператора імпорту:

importScripts ( 'script1.js', 'script2.js');

Субоб'екти Worker

Об'єкти Worker можуть створювати підлеглі елементи. Вони є зручним інструментом для подальшого розподілу великих завдань на етапі виконання. Однак субоб'екти Worker можна використовувати з деякими застереженнями.

  • Субоб'екти Worker слід розміщувати в межах того ж джерела, де розташовується вихідна сторінка.
  • В контексті субоб'ектов URI пов'язані з розташуванням вихідного об'єкта Worker (на відміну від головної сторінки).

Слід пам'ятати про те, що більшість браузерів створюють окремі процеси для кожного об'єкта Worker. Перш ніж створювати субоб'екти, необхідно простежити за тим, щоб пов'язані з ними процеси розумно використовували ресурси користувальницької системи. Їх нераціональне застосування може бути пов'язане з копіюванням повідомлень, переданих між головними сторінками і об'єктами Worker, замість їх спільного використання. Детальніше про це можна дізнатися в розділі Взаємодія з об'єктами Worker шляхом обміну повідомленнями .

Щоб навчитися створювати субоб'екти Worker, ознайомтеся з цим прикладом в специфікації.

Вбудовувані об'єкти Web Worker

Як вчинити в ситуації, коли необхідно сформувати скрипт об'єкта Web Worker в процесі роботи або створити автономну сторінку, не використовує окремі файли об'єктів? З новим інтерфейсом BlobBuilder об'єкти Web Worker можна "вбудовувати" в той же HTML-файл, в якому міститься основний логічний блок. Для цього створюються об'єкти BlobBuilder, в які код об'єктів Worker додається в якості рядка, як показано нижче.

// Prefixed in Webkit, Chrome 12, and FF6: window.WebKitBlobBuilder, window.MozBlobBuilder var bb = new BlobBuilder (); bb.append ( "onmessage = function (e) {postMessage ( 'msg from worker');}"); // Obtain a blob URL reference to our worker 'file'. // Note: window.webkitURL.createObjectURL () in Chrome 10+. var blobURL = window.URL.createObjectURL (bb.getBlob ()); var worker = new Worker (blobURL); worker.onmessage = function (e) {// e.data == 'msg from worker'}; worker.postMessage (); // Start the worker.

URL об'єктів Blob

Робота з інтерфейсом починається з виклику методу window.URL.createObjectURL () . Він створює просту рядок URL, яку можна використовувати для посилання на дані, що зберігаються в об'єкті File або Blob моделі DOM. наприклад:

blob: http: // localhost / c745ef73-ece9-46da-8f66-ebes574789b1

URL об'єктів Blob унікальні і зберігаються протягом усього часу виконання програми (наприклад, до вивантаження з пам'яті об'єкта document). Якщо необхідно створити багато URL об'єктів Blob, рекомендується видаляти непотрібні посилання. Зробити це можна шляхом їх передачі методу window.URL.revokeObjectURL () , Як показано нижче.

window.URL.revokeObjectURL (blobURL); // window.webkitURL.createObjectURL () in Chrome 10+.

У браузері Chrome є зручна сторінка для перегляду всіх створених URL об'єктів Blob: chrome: // blob-internals /.

повний приклад

Тепер розглянемо, як JavaScript-код об'єкта Worker вбудовується в сторінку. Для цього використовується тег <script>, що визначає об'єкт Worker.

<! DOCTYPE html> <html> <head> <meta charset = "utf-8" /> </ head> <body> <div id = "log"> </ div> <script id = "worker1" type = "javascript / worker"> // This script will not be parsed by JS engines because its type is javascript / worker. self.onmessage = function (e) {self.postMessage ( 'msg from worker'); }; // Rest of your worker code goes here. </ Script> <script> function log (msg) {// Use a fragment: browser will only render / reflow once. var fragment = document.createDocumentFragment (); fragment.appendChild (document.createTextNode (msg)); fragment.appendChild (document.createElement ( 'br')); document.querySelector ( "# log"). appendChild (fragment); } Var bb = new BlobBuilder (); bb.append (document.querySelector ( '# worker1'). textContent); // Note: window.webkitURL.createObjectURL () in Chrome 10+. var worker = new Worker (window.URL.createObjectURL (bb.getBlob ())); worker.onmessage = function (e) {log ( "Received:" + e.data); } Worker.postMessage (); // Start the worker. </ Script> </ body> </ html>

Такий підхід видається мені більш зрозумілим і правильним. Тег скрипта задається з параметрами id = "worker1" і type = 'javascript / worker' (тому браузер не виконує аналіз JavaScript-коду). Цей код витягується у вигляді рядка за допомогою методу document.querySelector ( '# worker1'). TextContent і передається в функцію BlobBuilder.append ().

Завантаження зовнішніх скриптів

При встановленні коду об'єкта Worker таким чином метод importScripts () приймає тільки абсолютні URI. Якщо спробувати передати йому відносний URI, браузер повідомить про помилку системи безпеки. Це пов'язано з тим, що об'єкт Worker (тепер створюваний з використанням URL елемента Blob) дозволяється з префіксом blob :, в той час як програма виконується в іншій схемі (як правило, http: //). Таким чином, помилка виникає через обмеження, пов'язаних з різним походженням.

Один із способів застосування методу importScripts () у вбудованому об'єкті Worker - вставка поточного URL основного скрипта шляхом його передачі об'єкту Worker і створення абсолютного URL вручну. Це забезпечить імпорт зовнішнього скрипта з того ж джерела. Припустимо, основна програма виконується в файлі http://example.com/index.html:

... <script id = "worker2" type = "javascript / worker"> self.onmessage = function (e) {var data = e.data; if (data.url) {var url = data.url.href; var index = url.indexOf ( 'index.html'); if (index! = -1) {url = url.substring (0, index); } ImportScripts (url + 'engine.js'); } ...}; </ Script> <script> var worker = new Worker (window.URL.createObjectURL (bb.getBlob ())); worker.postMessage ({url: document.location}); </ Script>

Обробка помилок

Як і будь-який логічний блок JavaScript, об'єкти Web Worker вимагають обробки будь-яких виникаючих помилок. Якщо помилка виникає при виконанні об'єкта, спрацьовує подія ErrorEvent. Інтерфейс містить три корисних властивості для визначення причини помилки: filename - назва скрипта об'єкта Worker, що викликав помилку, lineno - номер рядка, в якій виникла помилка, і message - її осмислене опис. Нижче наведено приклад налаштування обробника події onerror для друку властивостей помилки.

<Output id = "error" style = "color: red;"> </ output> <output id = "result"> </ output> <script> function onError (e) {document.getElementById ( 'error'). textContent = [ 'ERROR: Line', e.lineno, 'in', e.filename, ':', e.message] .join ( ''); } Function onMsg (e) {document.getElementById ( 'result'). TextContent = e.data; } Var worker = new Worker ( 'workerWithError.js'); worker.addEventListener ( 'message', onMsg, false); worker.addEventListener ( 'error', onError, false); worker.postMessage (); // Start worker without a message. </ Script>

Приклад: об'єкт workerWithError.js намагається виконати операцію 1 / x, де значення x не вказано.

запустити

workerWithError.js:

self.addEventListener ( 'message', function (e) {postMessage (1 / x); // Intentional error.};

Трохи про безпеку

Обмеження локального доступу

У зв'язку з обмеженнями безпеки в останніх версіях браузера Google Chrome об'єкти Worker не запускаються локально (наприклад, за адресою file: //). Замість цього вони припиняють роботу без будь-якого повідомлення. Для виконання програми в схемі file: // необхідно запустити Chrome з встановленої позначкою --allow-file-access-from-files. ПРИМІТКА. Не рекомендується запускати основний браузер з такою позначкою. Її слід використовувати тільки для перевірки, а не в режимі звичайного перегляду сторінок.

Це обмеження не стосується інших браузерів.

Рекомендації по використанню одного джерела

Скрипти об'єктів Worker повинні представляти собою зовнішні файли з тієї ж схемою, що і схема сторінки виклику. Тому неможливо завантажити скрипт з URL data: або javascript :, а зі сторінки https: не запускаються скрипти об'єктів Worker, що починаються з URL http :.

приклади використання

Які види програм використовують об'єкти Web Worker? На жаль, ці об'єкти є відносно новою технологією, і в більшості існуючих прикладів і посібників представлена ​​обробка простих чисел. Хоча це не саме захоплююче заняття, воно корисно для розуміння принципів роботи об'єктів Web Worker. Нижче наведено кілька ідей, про реалізацію яких можна подумати.

  • Попередній вибір або кешування даних для подальшого використання.
  • Синтаксичне виділення коду або інше форматування тексту в реальному часі.
  • Перевірка правопису.
  • Аналіз відео- і аудіо.
  • Фонові операції введення-виведення і опитування веб-служб.
  • Обробка великих масивів даних і об'ємних відповідей JSON.
  • Фільтрація зображень на елементі <canvas>.
  • Оновлення безлічі рядків в локальній веб-базі даних.

демонстраційні приклади

посилання

Дуже знайоме, чи не так?
Cmd': 'foobard', 'msg': '?