JavaScript Webbarbetare: en nybörjare's Guide
under 2019 har webbekosystemet utvecklats till den punkt där webbläsaren är en exekveringsmiljö för applikationer byggda på JavaScript. Detta återspeglas i den hastighet som branschen kommer med nya ramar, paradigmer, modullastare och buntare, beroendehanterare, byggverktyg och pakethanterare år efter år.
När JavaScript var tänkt i början av internet var riktningen för Internetutveckling inte tydlig. På grund av den ständiga, snabba förändringen i branschen och ekosystemet, behovet av bakåtkompatibilitet mellan webbläsare och webbstandarder, blev utvecklingen av JavaScript en konstant ström av patchar, hackar och eftertankar.
dagens mobila enheter levereras normalt med 8+ CPU-kärnor eller 12+ GPU-kärnor. Skrivbords-och serverprocessorer har upp till 16 kärnor, 32 trådar eller mer.
i den här miljön är det en flaskhals att ha en dominerande programmerings-eller skriptmiljö som är enkelgängad.
JavaScript är single-threaded
detta innebär att JavaScript — motorer — ursprungligen webbläsare-har en huvudgänga för körning, och för att uttrycka det enkelt kan process eller funktion B inte utföras förrän process eller funktion A är klar. En webbsidas användargränssnitt svarar inte på någon annan JavaScript — bearbetning medan den är upptagen med att utföra något-det här kallas DOM-blockering.
detta är fruktansvärt ineffektivt, särskilt jämfört med andra språk.
Om vi går till JS Bin och kör den här koden i webbläsarens JavaScript-konsol:
//noprotecti = 0;while (i < 60000) { console.log("The number is " + i); i++;}
… hela jsbin.com webbplatsen kommer inte att svara tills webbläsaren räknar – och loggar — till 60 000.
Vi kommer inte att kunna interagera med något på sidan, eftersom webbläsaren är upptagen.
Nu är detta en relativt krävande beräkningsprocess, och dagens webbappar involverar ofta mycket mer krävande uppgifter.
Vi måste kunna beräkna saker i bakgrunden medan användaren sömlöst interagerar med sidan.
Webbarbetare
W3C publicerade ett första utkast till web workers standard 2009. Den fullständiga specifikationen finns på webbplatsen Web Hypertext Application Technology Working Group-eller WHATWG-ett webbstandardorganalternativ till W3C.
Webbarbetare är ett asynkront system eller protokoll för webbsidor att utföra uppgifter i bakgrunden, oberoende av huvudtråden och webbplatsgränssnittet. Det är en isolerad miljö som är isolerad från window
objekt, document
objekt, direkt internetåtkomst och passar bäst för långvariga eller krävande beräkningsuppgifter.
bortsett från webbarbetare — ett system som är dedikerat till multitrådning — finns det andra sätt att uppnå asnykron bearbetning i JavaScript, till exempel asynkrona AJAX-samtal och händelseslinga.
för att visa detta kommer vi att gå tillbaka till JS Bin och prova detta utdrag:
console.log("A");setTimeout(function(){console.log("B");},2000);console.log("C");setTimeout(function(){console.log("D");},0);console.log("E");setTimeout(function(){console.log("F");},1000);
När vi kör detta är vår loggföljd A, C, E, D, F, B
. Webbläsaren schemalägger först operationer utan timeout, när de kommer, och sedan kör den setTimeout()
– funktionerna i ordningen för deras respektive angivna förseningar. Denna asynkronicitet bör dock inte sammanfogas automatiskt med multitrådning. Beroende på värdmaskinen kan detta ofta bara vara en enda trådbunt av samtalen i den ordning vi förklarade.
Webbarbetare & Multithreading
som Mozillas JavaScript-referenswebbplats förklarar är webbarbetare ett ” medel för webbinnehåll att köra skript i bakgrundstrådar.”
vi använder dem på följande sätt: vi kontrollerar tillgängligheten förWorker()
konstruktören i webbläsaren, och om den är tillgänglig instanserar vi ett arbetsobjekt med skriptets URL som argument. Detta skript kommer att utföras på en separat tråd.
skriptet måste serveras från samma värd eller domän av säkerhetsskäl, och det är också anledningen till att webbarbetare inte fungerar om vi öppnar filen lokalt med ett file://
– schema.
if (typeof(Worker) !== "undefined") { worker = new Worker("worker.js");}
nu definierar vi denna kod iworker.js
fil:
i = 0;while (i < 200000) { postMessage("Web Worker Counter: " + i); i++;}
Om du vill skriva högkvalitativa JavaScript Web worker-filer, kolla in vår bok, JavaScript: Best Practice.
separationen av trådar
en viktig sak att notera här är separationen av window
och document
exekveringsomfånget i huvudbläddrarens fönstertråd och worker
omfattning.
för att kunna använda trådenworker
måste dessa två omfattningar kunna kommunicera. För att uppnå detta använder vi postMessage()
— funktionen inom worker.js
— filen-för att skicka meddelanden till webbläsarens huvudtråd-och worker.onmessage
lyssnare i huvudtråden för att lyssna på worker
meddelanden.
Vi kan också skicka meddelanden från webbläsarens huvudtråd till worker
tråd eller funktion. Den enda skillnaden är att vi vänder om saker och kallar worker.postMessage()
på huvudtråden och onmessage
på arbetstråden. För att citera Mozillas utvecklarreferens:
Observera att
onmessage
ochpostMessage()
måste hängas avWorker
– objektet när det används i huvudskriptråden, men inte när det används i Arbetaren. Detta beror på att inom arbetaren är arbetaren effektivt den globala räckvidden.
Vi kan användaterminate()
– metoden på samma sätt för att avsluta vår arbetares utförande.
med allt detta i åtanke kommer vi till detta exempel:
index.html
<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Web Workers Example</title> <style type="text/css"> body {padding-top:28px;} .output-cont {margin-left:12%; margin-top:28px;} .output-cont h3 {width:200px; height:100%;} .output-cont button {padding:4px 8px; font-size:1.1rem; font-family:sans-serif; } </style></head><body><div class="output-cont"><button onclick="testWorker()">start worker</button><h3></h3><button onclick="terminateWorker()">terminate worker</button></div><br/><div class="output-cont"><button onclick="testMainThread()">start blocking thread</button><h3></h3></div><br/><div class="output-cont"><button onclick="alert('browser responsive!')">test browser responsiveness</button></div> <script> var worker; function testWorker() { if (typeof(Worker) !== "undefined") { if (typeof(worker) == "undefined") { worker = new Worker("worker.js"); } worker.onmessage = function(event) { document.getElementById("workerOutput").innerHTML = event.data; }; } else { document.getElementById("workerOutput").innerHTML = "Web Workers are not supported in your browser"; } } function terminateWorker() { worker.terminate(); worker = undefined; } function testMainThread() { for (var i = 0; i < 200000; i++) { document.getElementById("mainThreadOutput").innerHTML = "Main Thread Counter: " + i; } } </script></body></html>
och arbetare.js:
i = 0;while (i < 200000) { postMessage("Web Worker Counter: " + i); i++;}
detta ger oss möjlighet att testa effekterna av huvudtrådens utförande på sidbeteende och prestanda jämfört med webbarbetarens effekter.
i denna handledning använde vi http-server
för att betjäna filerna lokalt.
Nu kan vi se att arbetstråden inte blockerar interaktiviteten i huvudläsarprocessen, och looping genom 200 000 nummer påverkar inte huvudtråden. Siffrorna i#workerOutput
– elementet uppdateras på varje iteration.
blockeringstråden eller huvudtråden, när den är förlovad i en slinga, blockerar all interaktivitet (vi har ställt in antalet iterationer till 200 000 här, men det blir ännu tydligare om vi ökar det till 2 000 000).
en sak som pekar oss på en blockerad huvudtråd är att arbetsprocessen uppdaterar sidan på varje iteration, och slingan i huvudtråden (den som definieras i index.html
) uppdaterar bara #mainThreadOutput
– elementet på den sista iterationen.
detta beror på att webbläsaren är för förbrukad med räkning (for
loop) för att kunna rita om DOM, så det gör det bara när dess verksamhet med for
loop är helt klar (i slutet av slingan).
slutsats
i den här artikeln introducerade vi webbarbetare, en teknik som hjälper webbindustrin att hålla jämna steg med mer och mer krävande webbappar. Detta görs genom att tillhandahålla ett sätt för webbappar att utnyttja multi-processor och multi-threaded enheter genom att skänka några multi-threaded supermakter till JavaScript.
webbarbetare förvandlar mobila och stationära webbläsarmiljöer till applikationsplattformar, vilket ger dem en strikt exekveringsmiljö. Denna stränghet kan tvinga oss att tillhandahålla kopiering av objekt mellan flera trådar och att planera våra applikationer med dessa begränsningar i åtanke.