Signatur
Beschreibung
navigator.serial im Worker-Kontext (WorkerNavigator.serial) liefert dasselbe Serial-Objekt wie Navigator.serial im Fenster, nur eben innerhalb eines Dedicated Workers. Damit kannst du serielle Geräte wie Mikrocontroller, 3D-Drucker, Messgeräte oder USB-Seriell-Adapter außerhalb des Haupt-Threads ansprechen. So blockiert das Lesen und Parsen großer Datenströme die Benutzeroberfläche nicht.
Im Worker gibt es eine wichtige Einschränkung: Serial.requestPort() steht dort nicht zur Verfügung. Die Methode braucht eine Nutzeraktion und einen Auswahldialog, deshalb gibt es sie laut Spezifikation nur im Window. Der Nutzer muss den Port also zuerst im Haupt-Thread freigeben. Danach holt sich der Worker mit navigator.serial.getPorts() alle Ports, für die dieser Ursprung schon eine Berechtigung hat. Außerdem kann der Worker auf die Ereignisse connect und disconnect reagieren.
Typisches Vorgehen: Der Haupt-Thread ruft requestPort() nach einem Klick auf, der Worker sucht den passenden Port über getPorts() (z. B. anhand von getInfo()), öffnet ihn mit port.open() und verarbeitet die Daten über port.readable bzw. port.writable, also über Streams.
Rückgabewert
Serial-Objekt des Workers. Es bietet getPorts() sowie die Ereignisse connect und disconnect. Wird die API nicht unterstützt, ist die Eigenschaft undefined.Beispiele
Unterstützung im Worker prüfen
// worker.js
if ('serial' in navigator) {
const ports = await navigator.serial.getPorts();
postMessage({ type: 'status', supported: true, count: ports.length });
} else {
postMessage({ type: 'status', supported: false });
}
Port im Haupt-Thread freigeben, im Worker lesen
// main.js – Port nach einem Klick anfordern (geht nur im Window)
const worker = new Worker('serial-worker.js', { type: 'module' });
document.querySelector('#verbinden').addEventListener('click', async () => {
const port = await navigator.serial.requestPort({
filters: [{ usbVendorId: 0x2341 }] // z. B. Arduino
});
const { usbVendorId, usbProductId } = port.getInfo();
worker.postMessage({ usbVendorId, usbProductId });
});
worker.addEventListener('message', (e) => console.log('Gerät:', e.data));
// serial-worker.js
self.addEventListener('message', async ({ data }) => {
const ports = await navigator.serial.getPorts();
const port = ports.find((p) => {
const info = p.getInfo();
return info.usbVendorId === data.usbVendorId &&
info.usbProductId === data.usbProductId;
});
if (!port) return postMessage('Port nicht gefunden');
await port.open({ baudRate: 115200 });
const reader = port.readable
.pipeThrough(new TextDecoderStream())
.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
postMessage(value);
}
} finally {
reader.releaseLock();
}
});
Auf An- und Abstecken reagieren
// worker.js
navigator.serial.addEventListener('connect', (event) => {
postMessage({ type: 'connect', info: event.target.getInfo() });
});
navigator.serial.addEventListener('disconnect', (event) => {
postMessage({ type: 'disconnect', info: event.target.getInfo() });
});
// Wichtig · Fallstricke
- Kein
requestPort()im Worker: Die Berechtigung muss im Haupt-Thread über eine Nutzeraktion erteilt werden. Der Worker sieht nur Ports, die schon freigegeben sind, und bekommt sie übergetPorts(). - Nur Dedicated Worker: In Shared Workern und Service Workern gibt es die Eigenschaft nicht.
- Sicherer Kontext nötig: Die API funktioniert nur über HTTPS oder
localhost. Die Permissions-Policyserialkann sie außerdem sperren, z. B. in iframes. - Browser-Unterstützung: Experimentell und nur in Chromium-basierten Desktop-Browsern verfügbar. Firefox und Safari unterstützen die API nicht. Prüfe deshalb immer vorher mit
'serial' in navigator. - Portzugriff ist exklusiv: Ein Port kann nur einmal geöffnet sein. Wenn Haupt-Thread und Worker denselben Port öffnen wollen, schlägt der zweite Aufruf von
open()fehl. Vorport.close()musst du Reader und Writer mitreleaseLock()freigeben.