Oppretting av worker-tråder og utveksling av meldinger
Opprett workers, utveksle data med postMessage, og bruk workerData og MessageChannel til strukturert kommunikasjon.
Oppretting av worker-tråder og utveksling av meldinger er en gratis leksjon i Bootcamp i backendutvikling med Node.js på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Bootcamp i backendutvikling med Node.js, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Bootcamp i backendutvikling med Node.js inneholder totalt 4 leksjoner.
Derfor finnes Worker Threads
Node.js kjører JavaScript på én tråd. Det er utmerket for I/O-bundet arbeid (HTTP, databaser og filer) fordi hendelsesløkken aldri blokkeres. Men en CPU-bundet oppgave — endring av bildestørrelse, hashing, tolking av enorme nyttelaster eller kryptografi — kjører synkront og fryser hele serveren til den er ferdig.
- I/O-bundet → bli på hendelsesløkken og bruk asynkrone API-er.
- CPU-bundet → sett arbeidet bort til en
Worker-tråd slik at hovedtråden fortsatt kan betjene forespørsler.
Modulen worker_threads lar Dem kjøre JavaScript parallelt på separate operativsystemtråder, hver med sitt eget V8-isolat og sin egen hendelsesløkke.
Start Deres første Worker
De oppretter en tråd med new Worker(filename). Filen kjører på en ny tråd. Bruk isMainThread for å la én fil oppføre seg forskjellig avhengig av hvor den kjører.
Nedenfor starter hovedtråden en arbeider som peker tilbake på den samme filen. Arbeidergrenen utfører arbeidet og avslutter.
const { Worker, isMainThread } = require('node:worker_threads');
if (isMainThread) {
console.log('main: spawning worker');
const worker = new Worker(__filename);
worker.on('exit', (code) => console.log('worker exited with', code));
} else {
// This branch runs inside the worker thread
let sum = 0;
for (let i = 0; i < 1e7; i++) sum += i;
console.log('worker computed sum =', sum);
}Send inn data med workerData
For å overføre inndata til en arbeider når den opprettes, sender De workerData i options-objektet. Inne i arbeideren leser De det fra modulen worker_threads.
workerDatablir klonet (structured clone), ikke delt — endringer i arbeideren påvirker ikke hovedtråden.- Bruk det til de opprinnelige parameterne for jobben: en filbane, et tallintervall eller konfigurasjon.
const { Worker, isMainThread, workerData } = require('node:worker_threads');
if (isMainThread) {
new Worker(__filename, { workerData: { start: 1, end: 5 } });
} else {
const { start, end } = workerData;
let product = 1;
for (let i = start; i <= end; i++) product *= i;
console.log('factorial-ish product =', product);
}Hent resultater tilbake med postMessage
workerData går bare én vei og kan bare brukes én gang. For resultater bruker De meldingskanalen som alle arbeidere har innebygd:
- Inne i arbeideren:
parentPort.postMessage(value)sender data til forelderen. - På hovedtråden:
worker.on('message', cb)mottar dem.
Meldinger er asynkrone og strukturerte kloner, slik at hovedtråden forblir responsiv mens arbeideren beregner.
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, { workerData: 40 });
worker.on('message', (result) => {
console.log('main received:', result);
});
} else {
function fib(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
parentPort.postMessage(fib(workerData));
}Toveismeldinger
Kanalen fungerer i begge retninger. Forelderen kan bruke worker.postMessage(), og arbeideren lytter med parentPort.on('message', ...). Dette gjør en arbeider til en langlivet tjeneste som behandler mange jobber i stedet for én.
Merk at arbeideren fortsetter å kjøre mens den lytter etter meldinger; De må uttrykkelig be den om å stoppe (her utløser en null-sentinel process.exit).
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
worker.on('message', (r) => console.log('squared ->', r));
[2, 3, 4].forEach((n) => worker.postMessage(n));
worker.postMessage(null); // sentinel to stop
} else {
parentPort.on('message', (n) => {
if (n === null) { process.exit(0); }
parentPort.postMessage(n * n);
});
}Gjør én enkelt jobb om til en promise
I backend-kode ønsker De vanligvis en ryddig asynkron funksjon: kall den og bruk await på et resultat. Pakk arbeiderens livssyklus inn i en Promise, som fullføres ved message og avvises ved error eller en exit med annen verdi enn null.
Dette mønsteret er grunnlaget for enhver arbeiderpool: én promise per oppgave.
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');
function runJob(payload) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, { workerData: payload });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) reject(new Error('exit code ' + code));
});
});
}
if (isMainThread) {
runJob([5, 10, 15]).then((sum) => console.log('total =', sum));
} else {
const total = workerData.reduce((a, b) => a + b, 0);
parentPort.postMessage(total);
}Structured clone: hva De kan sende
Meldinger kopieres ved hjelp av structured clone-algoritmen, ikke JSON.stringify. Det betyr at De kan sende mer enn ren JSON.
- Støttes: objekter, tabeller,
Map,Set,Date,RegExp,ArrayBuffer, typede tabeller ogBigInt. - Støttes ikke: funksjoner, klasseinstanser med metoder, DOM-lignende objekter og alt som inneholder closures — dette utløser en
DataCloneError.
Kloning av store objekter koster CPU og minne. For store binærbuffere bør De foretrekke overføring fremfor kopiering (neste scene).
Overfør buffere i stedet for å kopiere dem
For store binærdata er kopiering sløsing med ressurser. Send en overføringsliste som det andre argumentet til postMessage. Eierskapet til ArrayBuffer flyttes til den andre tråden — uten kopiering — og bufferen blir ubrukelig hos avsenderen (dens byteLength blir 0).
Bruk dette for bildebytes, fildeler eller nyttelaster på megabyte-størrelse for å unngå å duplisere minnet.
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');
if (isMainThread) {
const buf = new Uint8Array([1, 2, 3, 4]).buffer;
const worker = new Worker(__filename, { workerData: buf }, { transferList: [buf] });
worker.on('message', (sum) => console.log('byte sum =', sum));
} else {
const bytes = new Uint8Array(workerData);
let sum = 0;
for (const b of bytes) sum += b;
parentPort.postMessage(sum);
}MessageChannel for sidekanaler
I tillegg til den innebygde parentPort kan De opprette Deres egen kanal med new MessageChannel(). Den gir to sammenkoblede porter, port1 og port2. Behold den ene, overfør den andre til en arbeider, og De har nå et dedikert rør — nyttig for å skille kontrollmeldinger fra data eller for kommunikasjon mellom arbeidere.
En MessagePort kan selv overføres, så De sender den i workerData eller i en melding med en overføringsliste.
const { Worker, isMainThread, MessageChannel, parentPort, workerData } =
require('node:worker_threads');
if (isMainThread) {
const { port1, port2 } = new MessageChannel();
new Worker(__filename, { workerData: { port: port2 }, transferList: [port2] });
port1.on('message', (msg) => {
console.log('main got on side channel:', msg);
port1.close();
});
port1.postMessage('ping');
} else {
const { port } = workerData;
port.on('message', (msg) => {
port.postMessage('pong (you said: ' + msg + ')');
});
}Håndter feil og avslutt ryddig
En arbeider kan krasje. Koble alltid opp hendelsene i livssyklusen slik at en mislykket jobb ikke i det stille lar en forespørsel bli hengende:
'error'— et ufanget unntak inne i arbeideren.'exit'— utløses én gang;codeer forskjellig fra null ved feil.'messageerror'— en melding kunne ikke deserialiseres.
For å stoppe en arbeider fra hovedtråden kaller De worker.terminate() (returnerer en promise). Arbeidere som ikke er avsluttet, holder prosessen i gang.
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
worker.on('error', (err) => console.log('caught worker error:', err.message));
worker.on('exit', (code) => console.log('exited, code =', code));
} else {
throw new Error('boom inside worker');
}En praktisk backend-struktur
I en ekte Node.js-backend starter De ikke en arbeider per forespørsel — oppstart av tråder er kostbart. I stedet:
- Behold en liten pool med langlivede arbeidere (ofte
os.cpus().length). - Send hver CPU-bundne jobb via
postMessageog koble resultater til forespørsler ved hjelp av enid. - Reserver arbeidere for reelt CPU-bundet arbeid; la I/O bli på hendelsesløkken.
Denne kodebiten viser meldingsprotokollen per arbeider, der en id brukes til å knytte forespørsel og svar sammen — kjerneideen som alle pool-biblioteker (som piscina) bygger på.
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
const pending = new Map();
let nextId = 0;
worker.on('message', ({ id, result }) => {
pending.get(id)(result);
pending.delete(id);
if (pending.size === 0) worker.terminate();
});
function submit(payload) {
return new Promise((res) => {
const id = nextId++;
pending.set(id, res);
worker.postMessage({ id, payload });
});
}
Promise.all([submit(3), submit(7)]).then((r) => console.log('results:', r));
} else {
parentPort.on('message', ({ id, payload }) => {
parentPort.postMessage({ id, result: payload * payload });
});
}Kort kontroll
De må sende en bildebuffer på 50 MB til en arbeider for behandling, og De vil unngå å duplisere minnet. Hvilken fremgangsmåte er riktig?
Oppsummering
De vet nå hvordan De starter arbeidere og flytter data mellom tråder:
- Start med
new Worker(file, { workerData }); velg gren basert påisMainThread. - Send resultater med
parentPort.postMessage(), og motta dem viaworker.on('message')— kanalen går begge veier. - Meldinger bruker structured clone (rikere enn JSON, men uten funksjoner); store buffere bør overføres via en overføringsliste for å unngå kopiering.
- Opprett ekstra rør med
MessageChannel, og overfør enMessagePortfor sidekanaler eller kanaler mellom arbeidere. - Håndter alltid
error/exitog kallterminate(); i produksjon bør De bruke en pool med meldinger knyttet til id-er i stedet for én arbeider per forespørsel.
Lær deg JavaScript med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 22
- Leksjoner
- 92
Ofte stilte spørsmål
Er leksjonen «Oppretting av worker-tråder og utveksling av meldinger» gratis?
Ja – hele teksten i «Oppretting av worker-tråder og utveksling av meldinger» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Bootcamp i backendutvikling med Node.js-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Bootcamp i backendutvikling med Node.js inneholder totalt 4 leksjoner.
Hva lærer jeg i «Oppretting av worker-tråder og utveksling av meldinger»?
Opprett workers, utveksle data med postMessage, og bruk workerData og MessageChannel til strukturert kommunikasjon. Du øver på Bootcamp i backendutvikling med Node.js med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Bootcamp i backendutvikling med Node.js?
Ingen tidligere erfaring er nødvendig. Bootcamp i backendutvikling med Node.js på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.
Hvor lang tid tar leksjonen «Oppretting av worker-tråder og utveksling av meldinger»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Bootcamp i backendutvikling med Node.js-leksjonen?
Ja. Alle Bootcamp i backendutvikling med Node.js-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Hvorfor event loop stopper ved CPU-bundet arbeid
- Oppretting av worker-tråder og utveksling av meldinger
- Deling av minne med SharedArrayBuffer og Atomics
- Bygging av en gjenbrukbar worker-pool for gjennomstrømming