Long Animation Frames API (LoAF) zur Diagnostik von Main-Thread-Blockaden
AI generated
60fps
ms
Web Performance / Browser APIs
Long Animation Frames API (LoAF) zur Diagnostik von Main-Thread-Blockaden
Endlich sehen, ob Rendering oder Skript die Interaktion verzögert

Die Long Animation Frames API liefert eine deutlich feinere Aufschlüsselung blockierender Frames als die ältere Long Tasks API und zeigt für jede lange Frame genau, welcher Anteil auf Skriptausführung und welcher auf Rendering entfällt. Damit wird sie zu einem zentralen Werkzeug für die präzise Diagnose von INP-Regressionen.

15 Min. Lesezeit Long Animation Frames API INP-Debugging

1. Rückblick: Die Long Tasks API und ihre Grenzen

Die Long Tasks API meldet über einen PerformanceObserver jede Aufgabe auf dem Main Thread, die länger als fünfzig Millisekunden dauert, und liefert dabei Start- und Endzeitpunkt sowie eine grobe Attribution, aus welchem Kontext, etwa einem bestimmten iframe, die Aufgabe stammte. Für viele Jahre war dies das einzige standardisierte Werkzeug, um lange blockierende Aufgaben überhaupt sichtbar zu machen, ohne auf manuelles Profiling in den DevTools angewiesen zu sein.

Die entscheidende Schwäche der Long Tasks API liegt jedoch in ihrer fehlenden inneren Aufschlüsselung: Sie meldet zwar, dass eine Aufgabe lange gedauert hat, aber nicht, welcher Anteil davon auf JavaScript-Ausführung, welcher auf Style-Neuberechnung und welcher auf Layout entfiel. Für die gezielte Fehlersuche bei Interaktionsverzögerungen war das oft zu grobkörnig, weshalb Entwickler häufig zusätzlich manuelles Profiling im Performance-Tab der DevTools benötigten.

2. Was ist die Long Animation Frames API

Die Long Animation Frames API, kurz LoAF, setzt genau an dieser Schwäche an und liefert für jede blockierende Animationsframe ab fünfzig Millisekunden eine detaillierte Aufschlüsselung der einzelnen Phasen. Registriert wird sie über einen PerformanceObserver mit dem Eintragstyp long-animation-frame, wobei jeder Eintrag Felder wie renderStart, styleAndLayoutStart sowie eine Gesamtdauer der Style- und Layout-Berechnung enthält.

Zusätzlich liefert jeder LoAF-Eintrag ein Array namens scripts mit einzelnen PerformanceScriptTiming-Objekten, die für jedes ausgeführte Skript innerhalb der Frame Angaben wie den Auslöser (invoker), die Quelldatei, die Funktionsposition und die individuelle Ausführungsdauer enthalten. Damit lässt sich erstmals ohne manuelles Profiling exakt bestimmen, welches konkrete Skript oder welcher Event-Handler für eine bestimmte Verzögerung verantwortlich war.

3. Code-Beispiel: PerformanceObserver für LoAF einrichten

Die Einrichtung folgt dem gewohnten Muster der Performance-Observer-APIs: Man registriert einen Observer für den Eintragstyp long-animation-frame und wertet in der Callback-Funktion die gemeldeten Einträge aus. Besonders wertvoll ist dabei das scripts-Array, das die Attribution auf einzelne Funktionsaufrufe erlaubt, statt nur die Gesamtdauer der Frame zu kennen.

Im folgenden Beispiel wird bei jeder erkannten langen Frame das jeweils teuerste Skript ermittelt und zusammen mit seiner Quelle protokolliert, was in der Praxis sehr schnell zu den tatsächlichen Verursachern von Interaktionsverzögerungen führt.


const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    const gesamtdauer = entry.duration;
    const renderAnteil = entry.styleAndLayoutStart
      ? entry.duration - (entry.styleAndLayoutStart - entry.startTime)
      : 0;

    // Teuerstes Skript innerhalb der Frame ermitteln
    const teuerstesSkript = entry.scripts
      .sort((a, b) => b.duration - a.duration)[0];

    console.warn('Lange Animation Frame erkannt', {
      dauerMs: gesamtdauer,
      renderingAnteilMs: renderAnteil,
      quelle: teuerstesSkript?.sourceURL,
      funktion: teuerstesSkript?.sourceFunctionName,
      ausloeser: teuerstesSkript?.invoker,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

4. Aufschlüsselung der Frame-Phasen im Detail

Ein LoAF-Eintrag zerlegt eine blockierende Frame in klar definierte Zeitabschnitte: die Zeit bis zum Start der Skriptausführung, die Skriptausführung selbst mit allen beteiligten Funktionsaufrufen, die anschliessende Style- und Layout-Berechnung und schließlich die Zeit bis zum tatsächlichen Rendering auf dem Bildschirm. Jede dieser Phasen ist als eigener Zeitstempel oder eigene Dauer im Eintrag abrufbar.

Besonders nützlich ist das Feld forcedStyleAndLayoutDuration innerhalb der einzelnen Skript-Einträge, das anzeigt, wie viel Zeit durch erzwungene synchrone Layout-Berechnungen verloren ging, die typischerweise entstehen, wenn ein Skript nach einer DOM-Änderung sofort eine Layout-Eigenschaft wie offsetHeight abfragt. Dieses sogenannte Layout Thrashing war mit der Long Tasks API praktisch unsichtbar und lässt sich mit LoAF nun gezielt aufspüren.

5. Rendering- versus Skript-Anteil richtig diagnostizieren

Ist der Skript-Anteil einer langen Frame dominant, deutet das meist auf ineffiziente JavaScript-Logik hin, etwa eine zu komplexe Berechnung in einem Event-Handler oder eine dritte Bibliothek, die synchron zu viel Arbeit verrichtet. In diesem Fall hilft das scripts-Array direkt weiter, weil es die genaue Quelle und Funktion benennt, die optimiert oder in kleinere Häppchen aufgeteilt werden sollte.

Ist hingegen der Style- und Layout-Anteil dominant, liegt die Ursache meist in umfangreichen DOM-Änderungen, komplexen CSS-Selektoren oder erzwungenem Reflow durch verschränkten Lese- und Schreibzugriff auf Layout-Eigenschaften. Hier hilft die genaue Zeitstempel-Analyse, um zu erkennen, ob das Problem tatsächlich am eigentlichen Rendering liegt oder an einem Skript, das dieses Rendering unnötig früh erzwingt.

6. Praktisches Debugging-Beispiel für eine INP-Regression

Angenommen, das Real-User-Monitoring zeigt nach einem Release einen deutlichen Anstieg des Interaction to Next Paint Wertes für den Klick auf den Warenkorb-Button. Der erste Schritt ist, die Event Timing API zu nutzen, um den genauen Zeitraum der betroffenen Interaktion zu ermitteln, und anschliessend alle LoAF-Einträge zu filtern, deren Zeitfenster sich mit dieser Interaktion überschneidet.

In der Praxis zeigt sich häufig, dass eine neu eingeführte Analytics-Bibliothek beim Klick synchron ein großes JSON-Objekt serialisiert, was im scripts-Array als dominanter Eintrag mit hoher Dauer auffällt, während der Rendering-Anteil der Frame kaum gestiegen ist. Ohne LoAF hätte die Long Tasks API lediglich eine längere Gesamtaufgabe gemeldet, ohne die verantwortliche Bibliothek konkret zu benennen, was die Fehlersuche erheblich verlängert hätte.

7. Integration in Real-User-Monitoring und web-vitals

Für den produktiven Einsatz lohnt es sich, LoAF-Daten nicht nur in der lokalen Konsole zu protokollieren, sondern aggregiert an ein Real-User-Monitoring-System zu senden, um systematische Muster über viele Nutzersitzungen hinweg zu erkennen. Die Google-Bibliothek web-vitals bietet dafür bereits experimentelle Attribution-Erweiterungen, die LoAF-Daten automatisch mit INP-Messungen verknüpfen.

Wichtig bei der Aggregation ist, nicht jeden einzelnen LoAF-Eintrag in voller Detailtiefe zu übertragen, da das schnell zu großen Datenmengen führt, sondern gezielt nur die Einträge zu sammeln, die tatsächlich mit einer schlechten Interaktion zeitlich überlappen. So bleibt die Datenmenge handhabbar, während trotzdem genügend Kontext für eine spätere Fehlersuche erhalten bleibt.

8. Browser-Support der Long Animation Frames API

Die Long Animation Frames API wird aktuell von Chrome und anderen Chromium-basierten Browsern unterstützt, während Firefox und Safari sie noch nicht implementiert haben. Da es sich um eine reine Diagnose-API ohne Auswirkung auf das Nutzererlebnis handelt, ist ein Feature-Test unkompliziert und die fehlende Unterstützung in anderen Browsern stellt kein funktionales Risiko dar.

Für Projekte, die Nutzer auf verschiedenen Browsern messen möchten, empfiehlt sich ein Fallback auf die weiterhin verfügbare Long Tasks API in nicht unterstützenden Browsern, auch wenn diese naturgemäß weniger detaillierte Daten liefert. So bleibt zumindest eine grobe Erkennung blockierender Aufgaben über alle Browser hinweg gewährleistet.

9. Fazit

Die Long Animation Frames API schließt eine lange bestehende Lücke zwischen dem groben Signal der Long Tasks API und dem aufwendigen manuellen Profiling in den DevTools, indem sie automatisiert und in Produktion nutzbar genau die Information liefert, die für INP-Debugging benötigt wird. Wer ernsthaft Interaktionsverzögerungen im Feld analysieren möchte, kommt an LoAF kaum noch vorbei.

Der praktische Mehrwert zeigt sich vor allem bei komplexen Anwendungen mit vielen Drittanbieter-Skripten, wo die Attribution auf einzelne Quelldateien und Funktionen den Unterschied zwischen stundenlanger Fehlersuche und einer in Minuten identifizierten Ursache ausmachen kann. Eine schrittweise Integration in bestehendes Real-User-Monitoring ist der naheliegende nächste Schritt für jedes Team, das INP ernst nimmt.

Merkmal Long Tasks API Long Animation Frames API
Schwellenwert 50ms Gesamtdauer 50ms Gesamtdauer der Frame
Skript-Attribution Nur grober Kontext (z.B. iframe) Einzelne Skripte mit Quelle und Funktion
Rendering-Anteil sichtbar Nein Ja, separat als eigene Phase
Erzwungenes Layout erkennbar Nein Ja, über forcedStyleAndLayoutDuration
Browser-Support Breit (inkl. Firefox teilweise) Aktuell Chromium-basierte Browser

Mironsoft

Web Performance, Core Web Vitals und Ladezeit-Optimierung

Ladezeiten, die Nutzer nicht abspringen lassen, bevor die Seite überhaupt sichtbar ist?

Wir prüfen bestehende Webseiten auf langsame Core Web Vitals, aufgeblähte JavaScript-Bundles und ungenutzte Render-Blocker und bauen daraus eine Performance-Grundlage, die messbar bleibt statt nur einmalig gut auszusehen.

Performance-Audit

Core Web Vitals, Ladewasserfall und Render-Blocker systematisch messen und beheben.

Bundle-Optimierung

JavaScript- und CSS-Bundle-Größe sowie Code-Splitting gezielt reduzieren.

Monitoring-Aufbau

Kontinuierliches Performance-Monitoring statt einmaliger Momentaufnahme etablieren.

10. Zusammenfassung

Long Animation Frames API

Zweck

Detaillierte Attribution blockierender Main-Thread-Frames

Neu ggü. Long Tasks

Aufschlüsselung nach Skript und Rendering

Anwendung

Gezieltes Debugging von INP-Regressionen

Support

Chromium-Browser, Firefox und Safari ausstehend

11. FAQ: Long Animation Frames API

1Was ist der Hauptunterschied zwischen LoAF und der Long Tasks API?
LoAF liefert eine detaillierte Aufschlüsselung nach Skript- und Rendering-Anteil inklusive einzelner Funktionsaufrufe, während die Long Tasks API nur die Gesamtdauer und einen groben Kontext meldet.
2Ab welcher Dauer erscheint ein LoAF-Eintrag?
Ebenso wie bei der Long Tasks API liegt der Schwellenwert bei fünfzig Millisekunden Gesamtdauer der Animationsframe, bevor ein Eintrag gemeldet wird.
3Was steht im scripts-Array eines LoAF-Eintrags?
Das scripts-Array enthält für jedes innerhalb der Frame ausgeführte Skript Angaben zu Auslöser, Quelldatei, Funktionsname und individueller Ausführungsdauer, was eine genaue Zuordnung ermöglicht.
4Wie erkenne ich erzwungenes Layout Thrashing mit LoAF?
Über das Feld forcedStyleAndLayoutDuration innerhalb der einzelnen Skript-Einträge, das die durch synchrone Layout-Abfragen verursachte Verzögerung explizit ausweist.
5Wie nutze ich LoAF für eine INP-Regression?
Man ermittelt über die Event Timing API den Zeitraum der betroffenen Interaktion und filtert anschliessend alle LoAF-Einträge, deren Zeitfenster sich damit überschneiden, um die verantwortlichen Skripte zu identifizieren.
6Welche Browser unterstützen die Long Animation Frames API?
Aktuell unterstützen Chrome und andere Chromium-basierte Browser LoAF vollständig, während Firefox und Safari die API noch nicht implementiert haben.
7Kann ich LoAF und die Long Tasks API parallel nutzen?
Ja, beide APIs können gleichzeitig registriert werden, was sich für einen Fallback in Browsern ohne LoAF-Unterstützung anbietet, um zumindest grobe Daten zu erhalten.
8Wie integriere ich LoAF-Daten in mein Monitoring?
Am sinnvollsten ist eine gefilterte Übertragung, bei der nur LoAF-Einträge gesammelt werden, die zeitlich mit einer schlecht gemessenen Interaktion überlappen, statt jeden Eintrag ungefiltert zu senden.
9Was bedeutet das Feld renderStart in einem LoAF-Eintrag?
Es markiert den Zeitpunkt, an dem die Rendering-Phase der Frame beginnt, und dient als Referenzpunkt, um den Skript-Anteil vom Rendering-Anteil der Gesamtdauer zu trennen.
10Ersetzt LoAF manuelles Profiling in den DevTools komplett?
Nicht ganz, aber LoAF reduziert den Bedarf an manuellem Profiling erheblich, da es die relevanten Attributionsdaten bereits automatisiert und produktionstauglich in Echtzeit liefert.