70-480 – Tworzenie procesu roboczego sieci Web

70-480

Uruchamianie i zatrzymywanie procesu roboczego sieci Web; przekazywanie danych do procesu roboczego sieci Web; konfiguracja limitów czasu i interwałów dla procesu roboczego sieci Web; rejestrowanie odbiornika zdarzeń dla procesu roboczego sieci Web; ograniczenia procesu roboczego sieci Web.



To już ostatni wpis z rozdziału Wdrażanie przepływu programu, a tym samym kolejne 25% materiału za nami. Można stwierdzić tym samym, że dotarliśmy do półmetka. Kolejne wpisy nie powstają tak szybko jakbym sam tego oczekiwał, ale staram się, uwierzcie.

Dzisiejszy temat będzie raczej przyjemny. Zajmiemy się kolejnym API, które pojawiło się wraz ze specyfikacją HTML5, a mianowicie Web Workers. Wykorzystanie Workerów polega na tym, że skrypt JavaScript może być uruchamiany w osobnym procesie (wątku), a tym samym nie blokuje nam on interfejsu użytkownika i zwraca odpowiedź, gdy ta jest gotowa. Jest to idealne rozwiązanie do uruchamiania zasobożernych części kodu JavaScript. Dzięki API Web Workerów możemy utworzyć taki proces, komunikować się z nim, otrzymywać informacje oraz zdalnie go zamknąć. Oczywiście nic nie jest kolorowe i Web Workery posiadają również swoje ograniczenia. Poznamy je na sam koniec dzisiejszego wpisu.

Tworzenie Workera

Standardowo kod JavaScript wykonuje się w jednym wątku i jest interpretowany przez silnik przeglądarki krok po kroku. Wszystkie procesy i zadania wykonywane są w ten sposób synchronicznie. By zadziałać wielowątkowo i uruchomić skrypt w osobnym wątku musimy zdefiniować obiekt workera:

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

Widzimy tutaj, że parametrem konstruktora jest String, który jest ścieżką do zewnętrznego pliku z kodem do uruchomienia wewnątrz nowo tworzonego wątku.

Konstruktor workera znajduje się w obiekcie window. Dostępność API sprawdzić możemy prostym warunkiem:

if (!!window.Worker) {
  ...
}

Wsparcie przeglądarek dla Web Workers.

Komunikacja z workerem i obsługa błędów

Komunikacja z wątkiem pobocznym następuje na zasadzie przesyłanych zdarzeń. By rozpocząć proces workera należy wywołać funkcję postMessage():

worker.postMessage();

Po stronie kodu worker.js musi istnieć obsługa eventu onmessage. W ten sposób worker rozpocznie swoje działanie i otrzyma parametr wysłany przez funkcję postMessage z wątku głównego:

// app.js
if (!!window.Worker) {
  var worker = new Worker('worker.js');
	
  worker.onmessage = function () {
    console.log(e.data);
    worker.postMessage('Ping!');
    worker.terminate();
  };
	
  worker.postMessage('Ping!');
}

// worker.js
this.addEventListener('message', function (e) {
  console.log(e.data);
  this.postMessage('Pong!');
}, false);

W kodzie worker.js może dojść do powstania błędu i  / lub rzucenia wyjątku. Błąd obsłużyć możemy poprzez rejestrację przechwytywania zdarzenia onerror:

worker.onerror = function () {
  console.log(e.message);
};

Spójrzmy na pełny przykład:

// app.js
if (!!window.Worker) {
  var worker = new Worker('worker.js');
	
  worker.onmessage = function () {
    console.log(e.data);
    worker.postMessage('Ping!');
  };
	
  worker.onerror = function () {
    console.log(e.message);
    worker.terminate();
};
	
  worker.postMessage('Ping!');
}

// worker.js
this.addEventListener('message', function (e) {
  console.log(e.data);
  this.postMessage('Pong!');
	
  throw new Error('Error occurred!');
}, false);

Zamykanie Workera

Działanie workera może zostać zatrzymane zarówno z wątku głównego poprzez wykonanie funkcji terminate() na workerze lub bezpośrednio w kodzie workera, który skończył już swoje zadanie lub odebrał odpowiednią wiadomość z procesu głównego.

Zakończenie wątku workera poprzez wątek główny:

worker.terminate();

Zakończenie wątku workera wewnątrz jego kodu, gdy wysłano parametr stop:

this.addEventListener('message', function (e) {
    if ('stop' === e.data) {
        this.close();
    }
}, false);

Służy do tego funkcja close().

Ograniczenia

Jako, że poboczny wątek musi być całkowicie wyizolowany i spełniać zasadę tzw. thread safe, to wewnątrz workera nie mamy dostępu do:

  • obiektów DOM – nie są thread safe
  • obiektu window
  • obiektu document
  • obiektu parent

Poza tym – funkcja postMessage ogranicza się do przyjmowania wartości String | JSON. Przekazanie obiektu do tej funkcji skutkuje konwersją do formatu JSON, co uniemożliwia nam przesyłanie funkcji.