Start · Sprachen · JavaScript · Referenz · Worker

Worker

Schnittstelle

Repräsentiert eine Hintergrundaufgabe in einem eigenen Thread, die aus einem Skript erzeugt wird und per <code>postMessage()</code> Nachrichten mit ihrem Ersteller austauscht.

seit JavaScript Baseline: alle Browser (Module-Worker: Baseline 20 Kategorie: webapi

Signatur

interface Worker extends EventTarget

Beschreibung

Das Interface Worker gehört zur Web Workers API. Ein Worker führt JavaScript in einem eigenen Thread aus, getrennt vom Haupt-Thread, der für die Oberfläche zuständig ist. Rechenintensive Aufgaben wie Bildverarbeitung, Parsen großer JSON-Dateien, Kryptografie oder Kompression blockieren dadurch die Benutzeroberfläche nicht.

Erzeugt wird ein Worker mit new Worker(url, options). Haupt-Thread und Worker teilen sich keinen Speicher (Ausnahme: SharedArrayBuffer). Sie kommunizieren über postMessage() und das Ereignis message. Die Daten werden dabei mit dem Structured-Clone-Algorithmus kopiert, oder bei übertragbaren Objekten wie ArrayBuffer, MessagePort oder OffscreenCanvas ohne Kopie übertragen.

Wichtige Mitglieder:

  • Worker.prototype.postMessage(message, transfer): sendet eine Nachricht an den Worker.
  • Worker.prototype.terminate(): beendet den Worker sofort, ohne dass er aufräumen kann.
  • Ereignis message / onmessage: Nachricht vom Worker ist eingetroffen.
  • Ereignis messageerror / onmessageerror: Nachricht konnte nicht deserialisiert werden.
  • Ereignis error / onerror: im Worker-Skript ist ein nicht abgefangener Fehler aufgetreten.

Im Worker selbst ist self ein DedicatedWorkerGlobalScope. Dort gibt es kein window und kein document, also keinen DOM-Zugriff. Verfügbar sind dagegen fetch, IndexedDB, WebSocket, Timer, importScripts() (nur in klassischen Workern) und viele weitere Web-APIs.

Parameter

Name Typ Default Beschreibung
$url Pflicht string | URL URL des Worker-Skripts. Das Skript muss gleichen Ursprungs sein (Same-Origin). Erlaubt sind außerdem blob:- und data:-URLs. Relative URLs werden relativ zum Dokument aufgelöst.
$options { type?: 'classic' | 'module', credentials?: 'omit' | 'same-origin' | 'include', name?: string } { type: 'classic', credentials: 'same-origin' } type: 'module' lädt das Skript als ES-Modul, dann funktionieren import/export. credentials steuert, ob beim Laden von Modul-Workern Cookies mitgesendet werden. name ist ein Bezeichner für Debugging und steht im Worker als self.name zur Verfügung.

Rückgabewert

Typ
Worker
Beschreibung
Eine neue Worker-Instanz. Das Skript wird asynchron geladen. Ist die URL ungültig, wird synchron ein SyntaxError geworfen, bei Verletzung der Same-Origin-Regel ein SecurityError. Scheitert das Laden selbst, wird ein error-Ereignis am Worker ausgelöst.

Beispiele

Worker erstellen und Nachrichten austauschen (Modul-Worker)

// main.js
const worker = new Worker(new URL('./primes.js', import.meta.url), {
  type: 'module',
  name: 'primzahlen'
});

worker.addEventListener('message', (event) => {
  console.log(`Gefunden: ${event.data.count} Primzahlen`);
  worker.terminate(); // Ressourcen freigeben
});

worker.addEventListener('error', (event) => {
  console.error('Worker-Fehler:', event.message, event.filename, event.lineno);
  event.preventDefault();
});

worker.postMessage({ limit: 100_000 });

// primes.js
self.addEventListener('message', ({ data }) => {
  const sieve = new Uint8Array(data.limit + 1);
  let count = 0;
  for (let i = 2; i <= data.limit; i++) {
    if (!sieve[i]) {
      count++;
      for (let j = i * i; j <= data.limit; j += i) sieve[j] = 1;
    }
  }
  self.postMessage({ count });
});
Gefunden: 9592 Primzahlen

Worker per Promise kapseln (Anfrage/Antwort mit IDs)

// main.js
const worker = new Worker('hash-worker.js');
let nextId = 0;
const pending = new Map();

worker.onmessage = ({ data }) => {
  const { id, result, error } = data;
  const { resolve, reject } = pending.get(id);
  pending.delete(id);
  error ? reject(new Error(error)) : resolve(result);
};

const callWorker = (payload) =>
  new Promise((resolve, reject) => {
    const id = nextId++;
    pending.set(id, { resolve, reject });
    worker.postMessage({ id, payload });
  });

// hash-worker.js
// self.onmessage = async ({ data }) => {
//   try {
//     const bytes = new TextEncoder().encode(data.payload);
//     const hash = await crypto.subtle.digest('SHA-256', bytes);
//     const hex = [...new Uint8Array(hash)].map(b => b.toString(16).padStart(2, '0')).join('');
//     self.postMessage({ id: data.id, result: hex });
//   } catch (e) {
//     self.postMessage({ id: data.id, error: e.message });
//   }
// };

const hash = await callWorker('hallo welt');
console.log(hash);
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

Große Daten ohne Kopie übertragen (Transferable)

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

const buffer = new ArrayBuffer(8 * 1024 * 1024); // 8 MB
console.log('Vorher:', buffer.byteLength);

// Das zweite Argument überträgt den Besitz, statt zu kopieren
worker.postMessage({ buffer }, [buffer]);

console.log('Nachher:', buffer.byteLength); // im Haupt-Thread nun leer
Vorher: 8388608 Nachher: 0

Inline-Worker aus einem Blob erzeugen

const source = `
  self.onmessage = ({ data }) => {
    self.postMessage(data.map((n) => n * n));
  };
`;
const blobUrl = URL.createObjectURL(new Blob([source], { type: 'text/javascript' }));
const worker = new Worker(blobUrl);

worker.onmessage = ({ data }) => {
  console.log(data);
  worker.terminate();
  URL.revokeObjectURL(blobUrl);
};

worker.postMessage([1, 2, 3, 4]);
[1, 4, 9, 16]

Siehe auch