Articles

Trådning av webben med modularbetare

JavaScript är enkelgängat, vilket innebär att det bara kan utföra en operation åt gången. Detta är intuitivt och fungerar bra för många fall på webben, men kan bli problematiskt när vi behöver göra tunga lyftuppgifter som databehandling, Tolkning, beräkning eller analys. Eftersom fler och mer komplexa applikationer levereras på webben finns det ett ökat behov av flertrådad bearbetning.

på webbplattformen är den viktigaste primitiva för gängning och parallellism web Workers API. Arbetare är en lätt abstraktion ovanpå operativsystemtrådar som exponerar ett meddelande som passerar API för kommunikation mellan trådar. Detta kan vara oerhört användbart när du utför kostsamma beräkningar eller arbetar på stora datamängder, vilket gör att huvudtråden kan fungera smidigt när du utför de dyra operationerna på en eller flera bakgrundstrådar.

Här är ett typiskt exempel på arbetaranvändning, där ett arbetskript lyssnar efter meddelanden från huvudtråden och svarar genom att skicka tillbaka egna meddelanden:

sida.js:

const worker = new Worker('worker.js');
worker.addEventListener(e => {
console.log(e.data);
});
worker.postMessage('hello');

arbetare.js:

addEventListener('message', e => {
if (e.data === 'hello') {
postMessage('world');
}
});

WEBBARBETARENS API har funnits i de flesta webbläsare i över tio år. Även om det betyder att arbetare har utmärkt webbläsarstöd och är väl optimerade, betyder det också att de länge föregår JavaScript-moduler. Eftersom det inte fanns något modulsystem när arbetare designades har API: et för att ladda kod i en arbetare och komponera skript förblivit liknande de synkrona skriptladdningsmetoderna som var vanliga 2009.

historia: klassiska arbetare #

Arbetskonstruktören tar en klassisk skriptadress, som är relativt dokumentets URL. Den returnerar omedelbart en hänvisning till den nya arbetaren instans, som exponerar ett meddelandegränssnitt samt en terminate() metod som omedelbart stannar och förstör arbetaren.

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

enimportScripts() funktionen är tillgänglig inom webbarbetare för att ladda ytterligare kod, men det pausar utförandet av arbetaren för att hämta och utvärdera varje skript. Det kör också skript i det globala omfånget som en klassisk<script> – tagg, vilket betyder att variablerna i ett skript kan skrivas över av variablerna i en annan.

arbetare.js:

importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
postMessage(sayHello());
});

hälsa.js:

// global to the whole worker
function sayHello() {
return 'world';
}

av denna anledning har webbarbetare historiskt infört en stor effekt på arkitekturen i en applikation. Utvecklare har varit tvungna att skapa smarta verktyg och lösningar för att göra det möjligt att använda webbarbetare utan att ge upp modern utvecklingspraxis. Som ett exempel, buntare som webpack bädda in en liten modul loader implementering i genererad kod som använder importScripts() för kod lastning, men wraps moduler i funktioner för att undvika variabla kollisioner och simulera beroende import och export.

ange modularbetare #

ett nytt läge för webbarbetare med ergonomi och prestandafördelar med JavaScript-moduler levereras i Chrome 80, kallad modularbetare. Worker constructor accepterar nu ett nytt{type:"module"} alternativ, som ändrar skriptladdning och körning för att matcha<script type="module">.

const worker = new Worker('worker.js', {
type: 'module'
});

eftersom modularbetare är standard JavaScript-moduler kan de använda import-och exportdeklarationer. Som med alla JavaScript-moduler utförs beroenden bara en gång i ett givet sammanhang (huvudtråd, arbetare etc.), och all framtida import hänvisar till den redan utförda modulinstansen. Lastning och utförande av JavaScript-moduler optimeras också av webbläsare. En moduls beroenden kan laddas innan modulen körs, vilket gör att hela modulträd kan laddas parallellt. Modulladdning cachar också analyserad kod, vilket innebär att moduler som används på huvudgängan och i en arbetare bara behöver analyseras en gång.

flytta till JavaScript-moduler möjliggör också användning av dynamisk import för lazy-loading kod utan att blockera utförandet av arbetaren. Dynamisk import är mycket tydligare än att använda importScripts() för att ladda beroenden, eftersom den importerade modulens export returneras snarare än att förlita sig på globala variabler.

arbetare.js:

import { sayHello } from './greet.js';
addEventListener('message', e => {
postMessage(sayHello());
});

hälsa.js:

import greetings from './data.js';
export function sayHello() {
return greetings.hello;
}

för att säkerställa bra prestanda är den gamla importScripts() – metoden inte tillgänglig inom modularbetare. Att byta arbetare för att använda JavaScript-moduler innebär att all kod laddas i strikt läge. En annan anmärkningsvärd förändring är att värdet på thisI Toppnivåområdet för en JavaScript-modul är undefined, medan i klassiska arbetare är värdet arbetarens globala omfattning. Lyckligtvis har det alltid funnits enself global som ger en hänvisning till det globala tillämpningsområdet. Den är tillgänglig i alla typer av arbetare inklusive servicearbetare, såväl som i DOM.

Modularbetare tar också bort stöd för HTML-stilkommentarer. Visste du att du kan använda HTML-kommentarer i webbarbetarskript?

Preload arbetare med modulepreload #

en väsentlig prestandaförbättring som följer med modularbetare är förmågan att förlasta arbetare och deras beroenden. Med modularbetare laddas och exekveras skript som standard JavaScript-moduler, vilket innebär att de kan förinstalleras och till och med analyseras med modulepreload:

<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">
<script>
addEventListener('load', () => {
// our worker code is likely already parsed and ready to execute!
const worker = new Worker('worker.js', { type: 'module' });
});
</script>

förinstallerade moduler kan också användas av både huvudtråden och modularbetarna. Detta är användbart för moduler som importeras i båda kontexterna, eller i fall där det inte är möjligt att veta i förväg om en modul kommer att användas på huvudtråden eller i en arbetare.

tidigare var alternativen för förladdning av webbarbetarskript begränsade och inte nödvändigtvis tillförlitliga. Klassiska arbetare hade sin egen” arbetare ” resurstyp för förladdning, men inga webbläsare implementerade <link rel="preload" as="worker">. Som ett resultat var den primära tekniken som var tillgänglig för förladdning av webbarbetare att använda <link rel="prefetch">, som helt och hållet förlitade sig på HTTP-cachen. När det användes i kombination med rätt cachningshuvuden gjorde det det möjligt att undvika att arbetarens instans måste vänta med att ladda ner arbetarens skript. Till skillnad från modulepreload denna teknik stödde emellertid inte förladdningsberoende eller pre-parsing.

vad sägs om delade arbetare? #

delade arbetare har uppdaterats med stöd för JavaScript-moduler från och med Chrome 83. Liksom dedikerade arbetare, konstruerar en delad arbetare med {type:"module"} alternativet laddar nu arbetarskriptet som en modul snarare än ett klassiskt skript:

const worker = new SharedWorker('/worker.js', {
type: 'module'
});

före stöd av JavaScript-moduler förväntade SharedWorker() konstruktören endast en URL och ett valfritt name argument. Detta kommer att fortsätta att fungera för klassisk delad arbetare användning; men skapa modul delade arbetstagare kräver att använda den nya options argument. De tillgängliga alternativen är desamma som för en dedikerad arbetare, inklusivename alternativ som ersätter föregåendename argument.

vad sägs om servicearbetare? #

tjänstearbetarspecifikationen har redan uppdaterats för att stödja att acceptera en JavaScript-modul som ingångspunkt, med samma {type:"module"} alternativ som modularbetare, men denna ändring har ännu inte implementerats i webbläsare. När det händer kommer det att vara möjligt att instansera en servicearbetare med en JavaScript-modul med följande kod:

navigator.serviceWorker.register('/sw.js', {
type: 'module'
});

nu när specifikationen har uppdaterats börjar webbläsare implementera det nya beteendet. Det tar tid eftersom det finns några extra komplikationer förknippade med att föra JavaScript-moduler till servicearbetare. Registrering av servicearbetare måste jämföra importerade skript med sina tidigare cachade versioner när man bestämmer om en uppdatering ska utlösas, och detta måste implementeras för JavaScript-moduler när de används för servicearbetare. Servicearbetare måste också kunna kringgå cacheminnet för skript i vissa fall när de letar efter uppdateringar.