Kurs obiektowego JavaScript (38)

Autor: Damian Chodorek • Opublikowany: 16 grudnia 2014 • Ostatnia aktualizacja: 28 grudnia 2014 • Kategoria: javascript, kursy

Web Workers – wątki w Twoich skryptach.
Znajdziesz również w wydaniu Software Developer’s Journal.

Z przyjemnością informuję, że mój artykuł, dotyczący technologii Web Workers w JS, pojawił się na łamach magazynu Software Developer’s Journal. Gorąco zachęcam więc do odwiedzenia strony sdjournal.pl oraz do zapoznania się z treścią magazynu.

Mój artykuł znajduje się na stronie 40, ale warto przeczytać (a przynajmniej przejrzeć) całe wydanie. Dlaczego? Ponieważ to doskonały sposób, aby poznać ciekawe technologie oraz dowiedzieć się o wydarzeniach z branży IT. W ten sposób można regularnie poszerzać swoje horyzonty i pogląd na nasz przemysł.

Poniżej publikuję prawie ten sam artykuł.

Web Workers

JavaScript to język, który został stworzony przy założeniu, iż nie należy wprowadzać w nim wielowątkowości. Przez długi czas sprytni programiści radzili sobie z brakiem tej technologii korzystając z funkcji setTimeout(), setInterval(), obiektu XMLHttpRequest oraz zdarzeń. W ten sposób naśladowali asynchroniczność i współbieżność.

Poważną wadą długo wykonujących się skryptów jest blokowanie interakcji z interfejsem użytkownika, a w niektórych przypadkach pojawienie się poniższego ostrzeżenia.

ostrzeżenie

HTML5 oferuje użycie prawdziwej wielowątkowości przy pomocy technologii Web Workers. Specyfikacja definiuje API umożliwiające uruchamianie skryptów w sposób współbieżny. Umożliwia to dłuższe wykonywanie się kodu, bez wpływu na komunikację z interfejsem użytkownika. Jakie korzyści płyną z tego faktu? Ogromne. Wydajność naszej aplikacji może zostać znacznie zwiększona, nie wspominając o znacznie wygodniejszym korzystaniu z niej. Należy pamiętać tylko kilka ważnych faktów.

Skrypty, które będą wykonywane współbieżnie, należy utożsamiać bardziej z systemowymi procesami niż wątkami. Te pierwsze uznaje się za ciężkie, te drugie za lekkie. Oznacza to, że instancje obiektów Web Workers powinny istnieć relatywnie długo oraz w małych ilościach. Jeśli zaczniemy tworzyć ich dziesiątki i co gorsza, tylko na chwilę, możemy zapomnieć o jakimkolwiek poprawieniu wydajności.

Specyfikacja definiuje dwa rodzaje obiektów: Dedicated Worker oraz Shared Worker. Pierwszy jest związany ze skryptem, w którym został utworzony i może komunikować się tylko z nim. Jeśli chodzi o obiekty Shared Worker, to skrypty wykonujące się w tle mogą komunikować się z innymi skryptami, a nie tylko z macierzystym. W artykule opisano jedynie obiekty typu Dedicated Worker, jako że są znacznie częściej wspierane przez przeglądarki.

Zastosowanie

Zanim zaczniemy, warto wspomnieć do czego można wykorzystać tę technologię. Poniżej kilka przykładów:

  • podświetlanie składni kodu lub dokonywanie innego rodzaju formatowania/analizy tekstu w czasie rzeczywistym,
  • dokonywanie analizy i modyfikacji plików multimedialnych w czasie rzeczywistym,
  • przeprowadzanie złożonych obliczeń, przykładowo na potrzeby gier.

Ograniczenia

Nietrudno domyślić się, że skoro twórcy JavaScript postanowili początkowo zrezygnować z wątków, to mieli ku temu dobry powód. W związku z tym, ze względu na zapewnienie bezpieczeństwa aplikacji wykorzystujących współbieżność, obiekty Web Workers nie mają dostępu do:

  • DOM,
  • obiektu window,
  • obiektu document,
  • obiektu parent.

Podstawy

Najbardziej powszechną praktyką jest umieszczanie kodu w osobnym pliku, którego adres należy podać jako parametr konstruktora obiektu Dedicated Worker. Komunikacja pomiędzy skryptem macierzystym, a wykonywanym w tle, zachodzi przy pomocy zdarzeń.

/*
plik – main.js
najpierw należy stworzyć obiekt, podając adres skryptu do konstruktora
*/
var worker = new Worker('task.js');

/*
następnie należy powiązać zdarzenie komunikatu z wywołaniem odpowiedniej funkcji, która go obsłuży
*/
worker.addEventListener('message', function(e){
  console.log('Wiadomosc od workera: ' + e.data);
}, false);

/*
w celu uruchomienia lub przekazania wiadomości do skryptu, należy wywołać metodę postMessage() z argumentem lub bez
*/
worker.postMessage('Witaj, swiecie!');
/*
plik – task.js
w skrypcie, który ma zostać uruchomiony w osobnym wątku, należy zdarzenie wiadomości połączyć z wywołaniem funkcji, która ma je obsłużyć; w poniższym przykładzie funkcja ta natychmiast prześle do skryptu głównego odpowiedź; komunikat zwrotny będzie zawierał dane, które otrzyma obiekt Web Worker
*/
self.addEventListener('message', function(e){
  self.postMessage(e.data);
}, false);

Powyższy przykład pokazuje jak wygląda dwustronna komunikacja pomiędzy skryptem głównym, a współbieżnym. W zależności od wsparcia przeglądarek, argumentem metody postMessage() może być obiekt typu String albo JSON. Większość nowoczesnych przeglądarek wspiera obydwie możliwości.

Dane przesyłane pomiędzy skryptami są kopiowane. Odbywa się to przy użyciu serializacji i deserializacji. Z tego powodu można zauważyć opóźnienie podczas przesyłania dużych ilości danych. Rozwiązaniem tego problemu jest zastosowanie Transferable Objects. Wtedy dane przenoszone są bez kopiowania z jednego kontekstu do drugiego. Należy myśleć o tym jak o referencji, ale pamiętając, że po przeniesieniu będą dostępne jedynie w skrypcie docelowym i nigdzie poza nim. Aby tego dokonać, wystarczy przekazać dane tak jak na poniższym przykładzie.

/*
szybkie przesłanie danych
*/
worker.postMessage(arrayBuffer, [arrayBuffer]);

Skrypty zewnętrzne

Brak dostępu do niektórych obiektów wbudowanych oraz zmiennych globalnych może być dość kłopotliwy. Przydatna staje się możliwość zaimportowania zewnętrznych skryptów i bibliotek przy pomocy funkcji importScripts(). Jej argumentami są lokalizacje skryptów, które mają być dołączone.

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

Obiekty inline

Istnieje możliwość tworzenia obiektów Web Workers bez konieczności umieszczania kodu w osobnym pliku. W tym celu należy skorzystać z obiektu Blob. Daje on możliwość uzyskania adresu URL, który będzie odnosił się do jego zawartości.

/*
najpierw należy utworzyć obiekt typu Blob; argumentem konstruktora jest ciąg znaków będący kodem, który wykona się współbieżnie
*/
var blob=new Blob(["onmessage=function(e){ postMessage('Wiadomosc od workera'); }"]);

/*
następnie należy pobrać adres URL stworzonego obiektu
*/
var blobURL=window.URL.createObjectURL(blob);

/*
po wykonaniu powyższych operacji możliwe jest utworzenie obiektu Web Worker przy pomocy pobranego wcześniej URL
*/
var worker = new Worker(blobURL);

Ten sposób posiada pewne ograniczenia. Przykładowo, jeśli korzystamy z funkcji importScripts(), adresy muszą być podane w postaci bezwzględnej.

Obsługa błędów

Mechanizm zdarzeń wykorzystany jest także do obsługi błędów, które mogą pojawić się w kodzie Web Workera. Wtedy uruchamiane jest zdarzenie o nazwie ErrorEvent i obiekt reprezentujący błąd, przekazywany jest do funkcji, która je obsługuje. Dzięki temu możemy łatwo uzyskać informacje o rodzaju błędu, pliku i linii, w której się pojawił.

/*
w kodzie skryptu głównego należy zarejestrować funkcję, która obsłuży zdarzenie błędu ('error')
*/
worker.addEventListener('error', function(e){
  alert('Wystapil blad w pliku ' + e.filename +
  ' w linii ' + e.lineno + '.' +
  ' Tresc bledu: ' + e.message);
});

Podsumowanie

Technologia Web Workers pojawiła się, ponieważ istniało zapotrzebowanie na takie funkcjonalności jak wielowątkowość w języku JavaScript, lepsze wykorzystanie mocy obliczeniowej sprzętu klienta oraz dokonywanie obliczeń niezależnie od interfejsu użytkownika. Obecne komputery charakteryzują się znacznie większymi możliwościami niż kiedyś. Umożliwia to przeniesienie pewnych operacji z serwerów na komputery klientów.

Sporym problemem jest brak pełnego wsparcia przez wszystkie przeglądarki, zwłaszcza jeśli chodzi o Shared Workers. Nie należy także lekceważyć potencjalnych zagrożeń, które sygnalizował twórca języka, Eich Brendan. Nieumiejętnie wykorzystana współbieżność, może być przyczyną wielu problemów.

Każdy, kto chce korzystać z dobrodziejstw tej technologii, musi pamietać, że tworzenie obiektów Web Workers jest stosunkowo kosztowne pod względem mocy obliczeniowej oraz innych zasobów komputera, jak np. pamięć. Stworzenie zbyt wielu obiektów może zabić wydajność, zamiast ją poprawić.

Źródła

Zobacz również
Kurs obiektowego JavaScript (33)Obsługa zdarzeń. Model bąbelkowy.
Kurs obiektowego JavaScript (34)Zapobieganie działaniu domyślnemu. Rodzaje zdarzeń.
Kurs obiektowego JavaScript (36)Popularny format JSON.
Kurs obiektowego JavaScript (37)Popularne biblioteki JS.
Projektowanie stron WWW (1)10 heurystyk Jakoba Nielsena – Twoja ściąga.
Kurs WordPress (1)Ogólny zarys technologii.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.