Wielowątkowość w JavaScript i HTML5 web workers

Program control

Poziom zaawansowany

„Skrypt nie odpowiada”, „Przerwij działanie skryptu” i tym podobne irytujące komunikaty. W końcu JavaScript idealny nigdy nie był i pewnie nie będzie. Mimo wszystko jego obecne możliwości i poziom optymalizacji imponują. Dużym krokiem naprzód w stronę większych możliwości były HTML5 Web Workers. Wiąże się to z kwestiami wielowątkowości, z której możemy korzystać w JavaScript!

Popracujmy w JavaScipt z HTML5 web workers

Mówiąc najprościej, web workers umożliwiają nam przeniesienie ciężkiej pracy (skomplikowanych, czasochłonnych operacji) do wykonania w tle, bez zawieszania bieżącej aplikacji (strony). Jest to rodzaj wielowątkowości (threading) stworzony dla aplikacji Web.

W podejściu klasycznym ciężki kod JavaScript wykonywany będzie w pojedynczym wątku, na jednym rdzeniu. Więc nie skorzystamy z zalet naszego świetnego komputera z najnowszym wielordzeniowym procesorem.

Pójdźmy zatem w kierunku web workers, aby móc tworzyć coraz to bardziej wyrafinowane aplikacje, mogąc wykonywać jednocześnie wiele różnych wątków.

Podstawowe elementy na których opiera się praca z web workers:

Wywołanie worker’a

var worker = new Worker("my_task.js");

Wątki nie dzielą swoich stanów ze stroną ani nie mogą komunikować się między sobą bezpośrednio. Do tego celu służy funkcja postMessage()

worker.postMessage("Hello, Work!");

Potrzebna jest jeszcze obsługa zdarzenia

worker.onmessage = function(event) {
 alert("Received message " + event.data);
 // doSomething ...
}

Działanie workera zakańczane jest funkcją terminate(), może być także wykonane wewnątrz kodu worker’a poprzez self.close(), co za chwilę zobaczymy w przykładzie.

Wczytywanie skryptów zewnętrznych umożliwia funkcja importScripts():


// my_task.js:
importScripts('script1.js');
importScripts('script2.js');

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

Przejdźmy teraz do uruchomienia workera w przykładowym kodzie.

Przykład implementacji

Tworzymy nowy plik HTML oraz skrypt my_task.js.

W pliku HTML umieszczamy podstawowe elementy:

...
    <button onclick="doHello()">Hello Command</button>
    <button onclick="doBye()">Bye Command</button>
    <button onclick="doDefault()">Unknown command</button>
    
    <button onclick="stop()">Stop the worker</button>
    <output id="result"></output>
...

A także kod JavaScript obsługujący wywołania naszego workera:

    function doHello() {
        worker.postMessage({'cmd': 'start', 'msg': 'Hello'});
    }

    function doBye() {
        worker.postMessage({'cmd': 'start', 'msg': 'Bye'});
    }
    
    function doDefault() {
        worker.postMessage({'cmd': 'def', 'msg': 'Excuse me, what?'});
    }

    function stop() {
        // worker.terminate();
        worker.postMessage({'cmd': 'stop', 'msg': 'STOP'});
    }
    
    // GO
    var worker = new Worker('my_task.js');
    worker.addEventListener('message', function(e) {
        document.getElementById('result').textContent = e.data;
    }, false);
    

Przejdźmy teraz do pliku my_task.js i dodajmy kod JavaScript, rozpoczynając od self.addEventListener:

self.addEventListener('message', function(e) {
    var data = e.data;
    
    switch (data.cmd) {
        // process "start" command
        case 'start':
        self.postMessage('WORKER STARTED: ' + data.msg);
        break;
        
        case 'stop':
        self.postMessage('WORKER STOPPED: ' + data.msg + '(off)');
        self.close(); // terminate the worker
        break;
        
        default:
        self.postMessage('Unknown command: ' + data.msg);
    };
}, false);

Stworzyliśmy event listener obsługujący wywołania naszego workera, oraz przykładowy kod reagujący na przesłane komendy.


W taki sposób możemy tworzyć wiele skomplikowanych wątków, pracujących w tle, bez obaw o zawieszanie się strony.

Demo do uruchomienia tutaj, natomiast kod do pobrania:

http://directcode.eu/samples/web_workers/web_workers.zip

Zasoby

Wpierw wspomnę o badaniu dostępności obsługi interesującej nas funkcjonalności.

Modernizr to biblioteka znana z poprzednich wpisów, wspomaga także sprawdzanie dostępności web workers w przeglądarce.

Przykład:


if (Modernizr.webworkers) {
  // window.Worker is available!
} 

// or
function supports_web_workers() {
  return !!window.Worker;
}

Ciekawsze przykłady zastosowań HTML5 web workers, takich jak generowanie fraktali na HTML5 Canvas:

http://simpledeveloper.com/mandelbrot/

To świetny przykład kodu.

Zasoby dla dociekliwych:

– From scratch

http://www.html5rocks.com/en/tutorials/workers/basics/

– MDN

https://developer.mozilla.org/pl/docs/Web/Guide/Performance/Uzycie_web_workers

– wiele różnorodnych, ciekawych przykładów zastosowań HTML5 Web Workers

http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html

Podsumowanie

HTML5 Web Workers to z pewnością interesujące rozszerzenie możliwości dostępnych w języku JavaScript. Powoli lecz systematycznie, dostępne z poziomu aplikacji Web staje się wszystko to, co do niedawna możliwe było jedynie w programowaniu na niższych warstwach abstrakcji.

Dziękuję za uwagę.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem. Bloger 🙂 Anty-lewak 🙂 Pasjonat programowania, nowych technologii, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook1Tweet about this on TwitterShare on Google+0Share on LinkedIn3Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.