Zum Hauptinhalt springen

[Lv3] Web Worker Anwendung: Hintergrundberechnung ohne UI-Blockierung

Web Worker ist eine API, die JavaScript in einem Hintergrund-Thread des Browsers ausführt und zeitaufwändige Berechnungen ermöglicht, ohne den Hauptthread (UI-Thread) zu blockieren.

Kernkonzept

Problemhintergrund

JavaScript ist ursprünglich single-threaded, der gesamte Code wird im Hauptthread ausgeführt:

// Zeitaufwändige Berechnung blockiert den Hauptthread
function heavyComputation() {
for (let i = 0; i < 10000000000; i++) {
// Komplexe Berechnung
}
return result;
}

// Die gesamte Seite friert während der Ausführung ein
const result = heavyComputation(); // UI kann nicht interagieren

Problem:

  • Seite eingefroren, Benutzer kann nicht klicken oder scrollen
  • Animationen stoppen
  • Extrem schlechte Benutzererfahrung

Web Worker Lösung

Web Worker bietet Multi-Threading-Fähigkeit für zeitaufwändige Aufgaben im Hintergrund:

// Worker für Hintergrundausführung verwenden
const worker = new Worker('worker.js');

// Hauptthread blockiert nicht, Seite bleibt interaktiv
worker.postMessage({ data: largeData });

worker.onmessage = (e) => {
console.log('Hintergrundberechnung abgeschlossen:', e.data);
};

Szenario 1: Verarbeitung großer Datenmengen

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

// Große JSON-Daten verarbeiten
worker.postMessage({ data: largeDataArray, action: 'process' });

worker.onmessage = function (e) {
console.log('Verarbeitungsergebnis:', e.data);
};

// worker.js
self.onmessage = function (e) {
const { data, action } = e.data;

if (action === 'process') {
const result = data.map((item) => {
return heavyComputation(item);
});

self.postMessage(result);
}
};

Szenario 2: Bildverarbeitung

Bildfilter, Komprimierung, Pixeloperationen - ohne UI-Einfrieren.

Szenario 3: Komplexe Berechnungen

Mathematische Operationen (Primzahlenberechnung, Verschlüsselung/Entschlüsselung) Hash-Berechnung großer Dateien Datenanalyse und Statistik

Einschränkungen und Hinweise

Was im Worker NICHT geht

  • Direkte DOM-Manipulation
  • Zugriff auf window, document, parent Objekte
  • Verwendung bestimmter Web APIs (wie alert)

Was im Worker GEHT

  • XMLHttpRequest / Fetch API
  • WebSocket
  • IndexedDB
  • Timer (setTimeout, setInterval)
  • Einige Browser-APIs
// Situationen, in denen Worker NICHT sinnvoll ist
// 1. Einfache, schnelle Berechnungen (Worker-Erstellung hat Overhead)
const result = 1 + 1; // Braucht keinen Worker

// 2. Häufige Kommunikation mit dem Hauptthread nötig
// Kommunikationskosten können den Multi-Threading-Vorteil aufheben

// Situationen, in denen Worker sinnvoll ist
// 1. Einmalige längere Berechnung
const result = calculatePrimes(1000000);

// 2. Batch-Verarbeitung großer Datenmengen
const processed = largeArray.map(complexOperation);

Reale Projektanwendungsfälle

Fall: Verschlüsselungsverarbeitung von Spieldaten

Auf der Spieleplattform müssen sensible Daten ver-/entschlüsselt werden:

// main.js - Hauptthread
const cryptoWorker = new Worker('/workers/crypto-worker.js');

function encryptPlayerData(data) {
return new Promise((resolve, reject) => {
cryptoWorker.postMessage({
action: 'encrypt',
data: data,
key: SECRET_KEY,
});

cryptoWorker.onmessage = (e) => {
if (e.data.success) {
resolve(e.data.encrypted);
} else {
reject(e.data.error);
}
};
});
}

const encrypted = await encryptPlayerData(sensitiveData);
// Seite friert nicht ein, Benutzer kann weiter operieren

// crypto-worker.js - Worker Thread
self.onmessage = function (e) {
const { action, data, key } = e.data;

try {
if (action === 'encrypt') {
const encrypted = performHeavyEncryption(data, key);
self.postMessage({ success: true, encrypted });
}
} catch (error) {
self.postMessage({ success: false, error: error.message });
}
};

Fall: Filterung großer Spieldatenmengen

const filterWorker = new Worker('/workers/game-filter.js');

const filters = {
provider: ['PG', 'PP', 'EVO'],
type: ['slot', 'live'],
minRTP: 96.5,
tags: ['popular', 'new'],
};

filterWorker.postMessage({
games: allGames, // 3000+ Spiele
filters: filters,
});

filterWorker.onmessage = (e) => {
displayGames(e.data.filtered);
};

// Hauptthread friert nicht ein, Benutzer kann weiter scrollen und klicken

Interview-Schwerpunkte

Häufige Interview-Fragen

F1: Wie kommunizieren Web Worker und Hauptthread?

A: Über postMessage und onmessage:

// Hauptthread -> Worker
worker.postMessage({ type: 'START', data: [1, 2, 3] });

// Worker -> Hauptthread
self.postMessage({ type: 'RESULT', result: processedData });

// Hinweis: Daten werden "strukturiert geklont" (Structured Clone)
// Übertragbar: Number, String, Object, Array, Date, RegExp
// Nicht übertragbar: Function, DOM-Elemente, Symbol

F2: Was ist der Performance-Overhead von Web Worker?

A: Zwei Hauptkosten:

// 1. Worker-Erstellungskosten (ca. 30-50ms)
const worker = new Worker('worker.js'); // Muss Datei laden

// 2. Kommunikationskosten (Datenkopie)
worker.postMessage(largeData); // Große Datenkopie braucht Zeit

// Lösungen:
// 1. Worker wiederverwenden (nicht jedes Mal neu erstellen)
// 2. Transferable Objects verwenden (Eigentumsübergabe, keine Kopie)
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
worker.postMessage(buffer, [buffer]); // Eigentum übertragen

F3: Was sind Transferable Objects?

A: Datenbesitz übertragen statt kopieren:

// Normaler Ansatz: Daten kopieren (langsam)
const largeArray = new Uint8Array(10000000); // 10MB
worker.postMessage(largeArray); // 10MB kopieren (zeitaufwändig)

// Transferable: Eigentum übertragen (schnell)
const buffer = largeArray.buffer;
worker.postMessage(buffer, [buffer]); // Eigentum übertragen (Millisekunden)

// Achtung: Nach der Übertragung kann der Hauptthread die Daten nicht mehr verwenden
console.log(largeArray.length); // 0 (bereits übertragen)

Unterstützte Transferable-Typen:

  • ArrayBuffer
  • MessagePort
  • ImageBitmap
  • OffscreenCanvas

F4: Wann sollte man Web Worker verwenden?

A: Entscheidungsbaum:

Ist es eine zeitaufwändige Berechnung (> 50ms)?
|- Nein -> Kein Worker nötig
|- Ja -> Weiter prüfen
|
|- Muss DOM manipuliert werden?
| |- Ja -> Worker nicht möglich (requestIdleCallback erwägen)
| |- Nein -> Weiter prüfen
|
|- Ist die Kommunikationsfrequenz hoch (> 60 Mal/Sekunde)?
|- Ja -> Vermutlich nicht geeignet (Kommunikationsoverhead)
|- Nein -> Geeignet für Worker

Geeignete Szenarien:

  • Verschlüsselung/Entschlüsselung
  • Bildverarbeitung (Filter, Komprimierung)
  • Sortierung/Filterung großer Datenmengen
  • Komplexe mathematische Berechnungen
  • Datei-Parsing (JSON, CSV)

Nicht geeignete Szenarien:

  • Einfache Berechnungen (Overhead größer als Nutzen)
  • Häufige Kommunikation nötig
  • DOM-Manipulation nötig
  • Nicht unterstützte APIs nötig

F5: Welche Typen von Web Worker gibt es?

A: Drei Typen:

// 1. Dedicated Worker (dediziert)
const worker = new Worker('worker.js');
// Kann nur mit der erstellenden Seite kommunizieren

// 2. Shared Worker (geteilt)
const sharedWorker = new SharedWorker('shared-worker.js');
// Kann von mehreren Seiten/Tabs geteilt werden

// 3. Service Worker (Dienst)
navigator.serviceWorker.register('sw.js');
// Für Cache, Offline-Unterstützung, Push-Benachrichtigungen

Vergleich:

EigenschaftDedicatedSharedService
SharingEinzelne SeiteMehrere SeitenGesamte Website
LebenszyklusSchließt mit der SeiteLetzte Seite schließtUnabhängig von Seite
HauptverwendungHintergrundberechnungSeitenübergreifende KommunikationCache, Offline

F6: Wie debuggt man Web Worker?

A: Chrome DevTools unterstützt:

// 1. Im Sources-Panel sind Worker-Dateien sichtbar
// 2. Breakpoints können gesetzt werden
// 3. Code kann in der Console ausgeführt werden

// Nützlicher Tipp: console im Worker verwenden
self.addEventListener('message', (e) => {
console.log('Worker empfangen:', e.data);
// Im DevTools Console sichtbar
});

// Fehlerbehandlung
worker.onerror = (error) => {
console.error('Worker-Fehler:', error.message);
console.error('Datei:', error.filename);
console.error('Zeile:', error.lineno);
};

Performance-Vergleich

Reale Testdaten (Verarbeitung von 1 Million Einträgen)

MethodeAusführungszeitUI friert ein?Speicher-Spitze
Hauptthread (synchron)2,5 sKomplett eingefroren250 MB
Hauptthread (Time Slicing)3,2 sGelegentliches Ruckeln280 MB
Web Worker2,3 sKomplett flüssig180 MB

Fazit:

  • Web Worker blockiert nicht nur die UI nicht, sondern ist auch schneller durch Multi-Core-Parallelität
  • Weniger Speicherverbrauch (Hauptthread muss große Datenmengen nicht vorhalten)

Verwandte Technologien

Web Worker vs andere Lösungen

// 1. setTimeout (Pseudo-Asynchron)
setTimeout(() => heavyTask(), 0);
// Immer noch im Hauptthread, wird ruckeln

// 2. requestIdleCallback (Ausführung in Leerlaufzeit)
requestIdleCallback(() => heavyTask());
// Nur in Leerlaufzeiten, keine Abschlussgarantie

// 3. Web Worker (echtes Multi-Threading)
worker.postMessage(task);
// Echt parallel, blockiert UI nicht

Comlink lässt Worker wie normale Funktionen verwenden:

// Traditionell (umständlich)
worker.postMessage({ action: 'add', a: 1, b: 2 });
worker.onmessage = (e) => console.log(e.data);

// Mit Comlink (schlank)
import * as Comlink from 'comlink';

const worker = new Worker('worker.js');
const api = Comlink.wrap(worker);

// Wie eine normale Funktion aufrufen
const result = await api.add(1, 2);
console.log(result); // 3

Lernempfehlungen

Interview-Vorbereitung:

  1. Verstehen, "warum Worker nötig ist" (Single-Thread-Problem)
  2. Wissen, "wann man ihn verwendet" (zeitaufwändige Berechnung)
  3. "Kommunikationsmechanismus" verstehen (postMessage)
  4. "Einschränkungen" kennen (kein DOM-Zugriff)
  5. Mindestens einen Worker-Fall implementiert haben

Praxistipps:

  • Mit einfachen Fällen beginnen (z.B. Primzahlenberechnung)
  • Chrome DevTools zum Debuggen nutzen
  • Performance-Unterschiede messen
  • Tools wie Comlink in Betracht ziehen

Verwandte Themen