Start · Sprachen · JavaScript · Referenz · ImageDecoder.completed

ImageDecoder.completed

Eigenschaft

Schreibgeschützte Eigenschaft mit einem <code>Promise</code>, das sich erfüllt, sobald der <code>ImageDecoder</code> alle codierten Bilddaten vollständig gepuffert hat.

seit JavaScript WebCodecs: Chrome/Edge 94+, Firefox 133+, in Safar Kategorie: webapi

Signatur

ImageDecoder.completed

Beschreibung

Die Eigenschaft completed gehört zum ImageDecoder-Interface der WebCodecs-API. Sie liefert ein Promise<undefined>, das sich erfüllt, sobald der Decoder alle codierten Bytes empfangen und gepuffert hat. Ab diesem Zeitpunkt ist auch die synchrone Eigenschaft ImageDecoder.complete auf true gesetzt.

Wichtig ist das vor allem, wenn die Bilddaten als ReadableStream übergeben werden, zum Beispiel direkt aus fetch(). Solange der Stream noch läuft, kann bei animierten Formaten (GIF, animiertes WebP, APNG, AVIF) der Wert track.frameCount weiter steigen. Erst nach await decoder.completed ist die Frame-Anzahl endgültig und jedes Frame lässt sich sicher decodieren.

Verwechseln Sie completed nicht mit decoder.tracks.ready. tracks.ready erfüllt sich schon, sobald genug Metadaten für die Track-Liste gelesen sind. completed wartet dagegen auf alle Daten. Sie brauchen completed, wenn Sie Gesamtzahlen anzeigen, alle Frames vorab decodieren oder erst nach dem vollständigen Laden eine Aktion starten wollen.

Das Promise wird abgelehnt, wenn der Decoder vorher mit close() geschlossen wird oder die Daten nicht gelesen bzw. decodiert werden können. Behandeln Sie deshalb immer den Fehlerfall.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das sich ohne Wert erfüllt, sobald alle codierten Daten gepuffert sind. Es wird mit einer DOMException abgelehnt, wenn der Decoder geschlossen wird oder ein Fehler beim Lesen bzw. Decodieren auftritt. Innerhalb einer Instanz ist es immer dasselbe Promise-Objekt.

Beispiele

Animiertes GIF streamen und endgültige Frame-Anzahl ermitteln

const response = await fetch('/img/animation.gif');

const decoder = new ImageDecoder({
  data: response.body, // ReadableStream
  type: 'image/gif'
});

// Metadaten sind da – die Frame-Anzahl kann aber noch steigen
await decoder.tracks.ready;
console.log('Vorläufig:', decoder.tracks.selectedTrack.frameCount);

// Warten, bis alle Bytes gepuffert sind
await decoder.completed;
console.log('complete:', decoder.complete);
console.log('Endgültig:', decoder.tracks.selectedTrack.frameCount);
Vorläufig: 3 complete: true Endgültig: 24

Alle Frames nach dem vollständigen Laden auf ein Canvas zeichnen

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const decodeAllFrames = async (url, type) => {
  const { body } = await fetch(url);
  const decoder = new ImageDecoder({ data: body, type });

  await decoder.completed; // Frame-Anzahl ist jetzt endgültig
  const { frameCount } = decoder.tracks.selectedTrack;

  for (let i = 0; i < frameCount; i++) {
    const { image } = await decoder.decode({ frameIndex: i });
    canvas.width = image.displayWidth;
    canvas.height = image.displayHeight;
    ctx.drawImage(image, 0, 0);
    image.close(); // VideoFrame-Speicher freigeben
  }

  decoder.close();
  return frameCount;
};

console.log('Frames:', await decodeAllFrames('/img/banner.webp', 'image/webp'));
Frames: 12

Fehlerbehandlung und Abbruch mit close()

const { body } = await fetch('/img/gross.gif');
const decoder = new ImageDecoder({ data: body, type: 'image/gif' });

// Nach 2 Sekunden abbrechen, falls das Laden zu lange dauert
const timer = setTimeout(() => decoder.close(), 2000);

try {
  await decoder.completed;
  console.log('Vollständig geladen');
} catch (err) {
  console.warn('Nicht abgeschlossen:', err.name);
} finally {
  clearTimeout(timer);
}
Nicht abgeschlossen: AbortError

// Wichtig · Fallstricke

  • Browser-Kompatibilität: ImageDecoder gibt es in Chromium-Browsern ab Version 94 und in neueren Firefox-Versionen. Safari unterstützt es nicht. Prüfen Sie die Unterstützung mit 'ImageDecoder' in window und über ImageDecoder.isTypeSupported(type). Die API ist nur in sicheren Kontexten (HTTPS) verfügbar, auch in Workern.
  • Bei ArrayBuffer/TypedArray als Datenquelle liegen alle Daten sofort vor. completed erfüllt sich dann praktisch sofort, und await ist nur bei Streams wirklich nötig.
  • completed heißt nicht „decodiert“: Das Promise sagt nur, dass die Daten gepuffert sind. Decodieren müssen Sie die Frames weiterhin mit decode(), und dabei können noch Fehler auftreten (zum Beispiel EncodingError bei beschädigten Daten).
  • Abgelehnte Promises: Rufen Sie close() auf, bevor alle Daten da sind, wird das Promise abgelehnt. Ohne catch gibt es dann eine „Unhandled Promise Rejection“.
  • Den Stream nicht vorher anderweitig lesen: Ein ReadableStream, der schon gesperrt oder gelesen ist, kann der Decoder nicht nutzen.
  • Mit decode() erzeugte VideoFrame-Objekte sollten Sie mit close() freigeben, sonst kann der Speicherverbrauch stark steigen.