Animatsiooniline veebiarendus: Jõudlus #3.1 – Dedicated Web Workers

Animatsiooniline veebiarendus: Jõudlus #3.1 – Dedicated Web Workers

Jõudluse kolmandates peatükkides võtame ette sellised toredad asjad nagu Workers.

Kahes esimeses alampeatükis (3.1 ja 3.2) võtame ette nii-öelda veebitöötajad (Web Workers) ja kolmandas Service Workers.

Veebitöötajatest üldisemalt

Põhimõte

Web Workers põhimõte on see, et saaks jooksutada osa JavaScripti tagaplaanil ehk teistes CPU harudes.

See on jõudluses väga oluline, sest see muudab veebilehe kiiremaks, kuna me ei käivita kõiki skripte ühes harus, vaid erinevates.

Nii ei pea visuaalide eest vastutav lõim (ehk haru) vastutama näiteks krüptimise eest.

Veebitöötajad saavad saata sõnumeid põhikoodi, et neid andmeid edasi kasutada.

Veebitöötajate tehniline pool

Nagu varem mainisin, siis Web Workers jooksutavad ennast teistes CPU harudes, kuid see ei ole kõik.

Nimelt kui lõim on teine, siis ei saa enam kasutada window objekti ega ka DOM’i. Kõik, mida veebitöötaja saab kasutada, peab talle põhiskriptist saatma, kuid ülejäänu on ikka sama.

Veebitöötajaid saab luua konstruktoriga (nagu näiteks Worker()) ning veebitöötaja saab luua endale omakorda alamtöötajaid.

Suhtlemiseks põhikoodiga on kaks peamist funktsiooni – postMessage(aMessage[, transferList]) ja onmessage(e).

Parameetrid seletan hiljem.

Olemas on kahte sorti veebitöötajaid (kui Service Workers välja arvata) – Dedicated Workers (ehk ühest kohast ligipääsetavad töötajad; teen täna juttu just nendest) ja Shared Workers (ehk igalt poolt kätte saadavad töötajad).

Reaalne kasutus

Tegelikkuses ei ole veebitöötajad midagi keerulist. See on nagu lapsemäng (kui JavaScriptist ka midagi jagad).

Dedicated Workers

Nagu varem ütlesin, et veebitöötajaid saab luua läbi konstruktori.

Dedicated Workeri (edaspidi ka pühendunud veebitöötaja on töötaja, millele pääseb ligi ainult sellest skriptist, kus ta defineeriti ehk põhikoodist) konstruktoriks on Worker(worker.js), mille parameetriks on veebitöötaja JavaScripti faili URL.

Ehk osa JavaScriptist peab olema teises failis.

Sellest, et töötaja JS peab olema eraldi failis, on võimalik ka hoiduda, kuid lihtsuse mõttes ei räägi ma sellest siin.

Uue töötaja loomine on imelihtne:

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

worker.js on siis eraldi fail, kus asub veebitöötaja loogika.

Suhtlus veebitöötaja ja põhiskripti vahel käib läbi postMessage(aMessag[, transferList]) ja onmessage(e) funktsioonide läbi.

postMessage() funktsiooni kasutatakse, et saata põhiskriptist andmeid veebitöötajale ja vice versa. Sellega ka tegelikult käivitatakse veebitöötaja.

postMessage() funktsioonil on kaks parameetrit:

  • aMessage – andmed, mida soovime edasi saata. Edasi võib saata ükskõik, mis tüüpi andmeid (sõne, numbreid, objekte jne).
  • transferList – mittekohustuslik; massiiv erinevatest objektidest, et vahetada antud objektide omanikku. Massiivi liikmeteks sobivad ainult Transferable objektid.

    onmessage() lubab meil veebitöötaja poolt andmeid kasutada ning ka vastupidi – me saame veebitöötajas põhikoodi poolt saadetud andmeid töödelda.

Kuna onmessage() on osa sündmusetöötlejast, siis tema parameetriks on sõnumi sündmusetöötleja objekt (tihti tähistatud, kas tähega “e” või lihtsalt “event”), millel on olemas omadus data, mis tähistab töötaja/põhiskripti poolt saadetud sõnumit.

Selleks, et CPU mitte ülekoormata, saame ka veebitöötajaid peatada läbi funktsioonide worker.terminate() ja close().

Tegemist on põhimõtteliselt samade funktsioonidega, kuid terminate() tuleb kasutada siis kui peatame veebitöötaja väljaspool veebitöötaja koodi (ehk põhiskriptis) ning close() funktsiooni siis, kui peatame veebitöötaja ise tema koodis.

Kui veebitöötaja on peatatud ei saa seda enam mitte kunagi kasutada. Seega peaks teadma täpselt, millal veebitöötaja töö lõpetatakse.

Vahete vahel võib tekkida olukordi, kus veebitöötaja kasutab mingit teist skripti enda töö tegemiseks.

Selleks, et need oleksid Web Workerile saadaval, on olemas funktsioon importScripts(), mille argumentideks on komadega eraldatud (kui teisi faile on mitu) URId sõnedena, mis viitavad välistele skriptifailidele.

Kui peaks juhtuma, et veebitöötajas tekib viga, siis käivitatakse automaatselt onerror() funktsioon, mille data objektis on kolm tähtsamat omadust:

  • message – veateade
  • filename – faili nimi, kus viga tekkis
  • lineno – reanumber

See ongi pühendatud töötajatega tegelikult kõik. Rohkem keerulist siin pole midagi.

Kood koos kommentaaridega

Põhiskript (main.js):

//Defineerime algmuutujad
var text = "Hello";

//Loome uue pühendunud veebitöötaja
var worker = new Worker("worker.js");

//Käivitame veebitöötaja läbi parameetri text (liigu edasi siit worker.js koodi)
worker.postMessage(text);

//Loome sõnumi sündmusetöötleja, et saada sõnumeid/andmeid loodud veebitöötajast
worker.onmessage = function(e){
	console.log(e.data); //"Hello Worker"
}

Pühendunud veebitöötaja (worker.js):

//Väliste teekide/failide impordimine töötajasse
importScripts("foo.js");
importScripts("//naide.com/bar.js");

//Loome sõnumi sündmusetöötleja, et saada sõnumeid/andmeid põhiskriptist
//Tegemist on tihti ka veebitöötaja protsesside kävitajaks
onmessage = function(e){
	var msg = e.data; //Põhiskritpist saadetud sõnum - "Hello"
	msg += " Worker"; //"Hello Worker"
	postMessage(msg); //Saadame muutuja msg tagasi põhiskripti
	close(); //Peatame töötaja
}

Võib olla ka märkasid, et kasutasin põhiskriptis pidevalt worker.fun(), kuid veebitöötajas lihtsalt antud funktsiooni. See on nii sellepärast, et kuna veebitöötaja on eraldi CPU lõimus, siis on veebitöötaja ka automaatselt globaalses scope‘is (saame funktsioonidele otse ligi).

Lõpetuseks toon ma välja natukene tõsisema näite veebitöötajate kasutusest.

Tegemist on küll näitega, kuid arvan, et see aitab sul asjdest paremini aru saada.

HTML (index.html):

<!DOCTYPE html>
<html>
	<head>
		<title>Web Workers</title>
		<meta charset="UTF-8" />
	</head>
	<body>
		<h1>Näide veebitöötajate kasulikkusest</h1>
		<h2>Ole selle näite kasutamisel ettevaatlik, kuna "Krüpti ilma veebitöötajata" toiming võib sinu brauseri kinni jooksutada!</h2>
		<h3>Vajadusel muuda "main.js" failis olevat muutajat "loopLength" väiksemaks.</h3>
		<!--Nupp, mis käivitab krüptimise ilma veebitöötajata-->
		<button id="start-button-normal">Krüpti ilma veebitöötajata</button>
		<!--Nupp, mis käivitab krüptimise koos veebitöötajaga-->
		<button id="start-button-worker">Krüpti koos veebitöötajaga</button>
		<!--Animatsioonikast, et näidata veebitöötajate kasulikkust-->
		<div id="animation-box" style="width: 100px; height: 100px; background-color: #009933; margin-top: 20px; border-radius: 20px;"></div>

		<!--CryptoJS-->
		<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" type="text/javascript"></script>
        <!--VelocityJS-->
        <script src="https://cdn.rawgit.com/julianshapiro/velocity/master/velocity.min.js" type="text/javascript"></script>
        <!--Põhi JavaScripti fail-->
		<script src="main.js" type="text/javascript"></script>
	</body>
</html>

Põhiskript (main.js):

//Nupud
var startButtonWithoutWorker = document.getElementById("start-button-normal");
var startButtonWorker = document.getElementById("start-button-worker");

//Veebitöötaja
var worker = new Worker("encrypt-worker.js");

//Kui mitu korda, me salasõnu krüptime
var loopLength = 5000;

//"startButtonWithoutWorker" nupu vajutamisel käivita siinne encrupt funktsioon
startButtonWithoutWorker.onclick = function(){
	encrypt(loopLength);
};

//"startButtonWorker" nupu vajutamisel käivita veebitöötaja
startButtonWorker.onclick = function(){
	worker.postMessage(loopLength);
};

//Saa andmed veebitöötajast
worker.onmessage = function(e){
	console.group("Krüptimine veebitöötajaga");
	console.log(e.data.msg);
	console.log(e.data.ex);
	console.groupEnd("Krüptimine veebitöötajaga");
};

//Krüpti ühes ja samas harus, milles on brauser
function encrypt(length){
	var encryptedPasswords = [];


	for(var i = 0; i !== length; i++){
		
		encryptedPasswords[i] = CryptoJS.AES.encrypt((Math.random()*Math.random()).toString(), "Secret Passphrase");
	}

	console.group("Krüptimine ilma veebitöötajata");
	console.log("Valmis krüptimine ilma veebitöötajata");
	console.log("Näide - " + encryptedPasswords[0]);
	console.groupEnd("Krüptimine ilma veebitöötajata");
}


//Animatsioon
Velocity(document.getElementById("animation-box"), {translateZ: 0, translateX: "200px", rotateZ: "180deg"}, {duration: 1000, loop: true});

Veebitöötaja kood (encrypt-worker.js.js):

//Impordi CryptoJS
importScripts("https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js");

//Krüpti salasõnu
function encrypt(length){
	var encryptedPasswords = [];
	for(var i = 0; i !== length; i++){
		encryptedPasswords[i] = CryptoJS.AES.encrypt((Math.random()*Math.random()).toString(), "Secret Passphrase");
	}

	//Saada andmed tagasi põhiskripti
	postMessage({msg: "Valmis krüptimine veebitöötajaga", ex: "Näide - " + encryptedPasswords[0]});
}


onmessage = function(e){
	//Alusta krüptimisega
	encrypt(e.data);
}

Sama lähtekoodi võid leida siit ning selle reaalse näite siit – nagu võid näha, et kui klikkad “Krüpti ilma veebitöötajata”, siis animatsioon jäätub täielikult ehk veebileht pole üldse kasutuskõlbulik krüpteerimise ajal. Veebitöötajatega seda probleemi pole.


Ja ongi pühendunud veebitöötajatega ühel pool.

Kui sul tekkis küsimusi, siis küsi neid julgelt kommenteerides! 🙂


Senikaua ole tugev ja kohtume juba järgmistes postitustes,

Tähelepanu eest tänades – Oliver Paljak

P.S! Kui oled huvitatud animatsioonilisest veebilehest, siis võta minuga julgelt ühendust AnsiVeebi kodulehel.
Ma ei pea ennast praegu animatsioonilise veebiarenduse eksperdiks, kuid olen sellele spetsialiseerunud ja saan Sind väga palju aidata animatsioonilise veebilehe loomisel!

Leave a Reply

Your email address will not be published. Required fields are marked *