Start · Sprachen · JavaScript · Referenz · PerformanceScriptTiming

PerformanceScriptTiming

Schnittstelle

Liefert Messwerte zu einem einzelnen Skript, das zu einem langen Animationsframe (LoAF, über 50 ms) beigetragen hat: Quelle, Aufrufer, Dauer und erzwungene Layout-Zeit.

seit JavaScript Chrome/Edge 123+ (Long Animation Frames API), nich Kategorie: webapi

Signatur

interface PerformanceScriptTiming extends PerformanceEntry

Beschreibung

PerformanceScriptTiming gehört zur Long Animation Frames API (LoAF). Ein langer Animationsframe liegt vor, wenn das Rendern eines Frames länger als 50 ms dauert. Jeder solche Frame wird als PerformanceLongAnimationFrameTiming-Eintrag gemeldet. Dessen Eigenschaft scripts enthält ein Array von PerformanceScriptTiming-Objekten: eines für jedes Skript, das mindestens 5 ms zur Laufzeit des Frames beigetragen hat.

Die Schnittstelle beantwortet die zentrale Frage beim Debuggen schlechter Interaction to Next Paint-Werte (INP): Welcher Code hat den Hauptthread blockiert? Du erfährst, welche Datei (sourceURL), welche Funktion (sourceFunctionName) und welcher Auslöser (invoker, z. B. BUTTON#buy.onclick) verantwortlich war, wie lange das Skript lief und wie viel Zeit davon auf erzwungenes, synchrones Style- und Layout-Neuberechnen (Layout Thrashing) entfiel.

Die Objekte erzeugst du nicht selbst, es gibt keinen Konstruktor. Du bekommst sie über einen PerformanceObserver mit dem Typ "long-animation-frame". Typische Einsatzgebiete sind Real-User-Monitoring (RUM), die Zuordnung von Leistungsproblemen zu Drittanbieter-Skripten und gezielte Optimierung in eigenen Event-Handlern.

Wichtige Eigenschaften (alle schreibgeschützt), zusätzlich zu den von PerformanceEntry geerbten:

  • entryType: immer "script". name: ebenfalls "script".
  • startTime / duration: Beginn und Gesamtdauer des Skripts in Millisekunden. Die Dauer schließt anschließend abgearbeitete Microtasks ein.
  • executionStart: Zeitpunkt, ab dem das Skript tatsächlich ausgeführt wurde, also nach dem Kompilieren.
  • forcedStyleAndLayoutDuration: Zeit für erzwungene synchrone Style- und Layout-Berechnungen.
  • pauseDuration: Zeit in synchron blockierenden Aufrufen wie alert() oder synchronem XHR.
  • invoker / invokerType: Auslöser und dessen Typ ("classic-script", "module-script", "event-listener", "user-callback", "resolve-promise", "reject-promise").
  • sourceURL, sourceFunctionName, sourceCharPosition: Herkunft im Quellcode.
  • window / windowAttribution: das zugehörige Fenster und seine Beziehung zum aktuellen Dokument ("self", "descendant", "ancestor", "same-page", "other").

Parameter

Name Typ Default Beschreibung
$invoker Pflicht string Beschreibt den Auslöser, z. B. "BUTTON#buy.onclick", "Window.setTimeout", "Response.json.then" oder die Skript-URL. (Eigenschaft, kein Funktionsparameter)
$invokerType Pflicht "classic-script"|"module-script"|"event-listener"|"user-callback"|"resolve-promise"|"reject-promise" Art des Einstiegspunkts, über den das Skript lief.
$executionStart Pflicht DOMHighResTimeStamp Beginn der eigentlichen Ausführung nach dem Kompilieren. executionStart - startTime entspricht ungefähr der Parse- und Kompilierzeit.
$forcedStyleAndLayoutDuration Pflicht DOMHighResTimeStamp Summe der Zeit für erzwungene synchrone Style- und Layout-Berechnungen, etwa durch das Lesen von offsetHeight direkt nach DOM-Änderungen.
$pauseDuration Pflicht DOMHighResTimeStamp Zeit in synchron pausierenden Operationen, z. B. alert(), confirm() oder synchronem XMLHttpRequest.
$sourceURL Pflicht string URL des Skripts. Leerer String, wenn sie nicht ermittelbar ist.
$sourceFunctionName Pflicht string Name der ausgeführten Funktion. Bei anonymen Funktionen leer.
$sourceCharPosition Pflicht number Zeichenposition im Skript. -1, wenn unbekannt.
$window Pflicht Window|null Das Fenster, zu dem das Skript gehört. null bei Cross-Origin-Fenstern bzw. wenn es nicht zugänglich ist.
$windowAttribution Pflicht "self"|"descendant"|"ancestor"|"same-page"|"other" Beziehung des Skript-Fensters zum aktuellen Dokument, z. B. "descendant" für ein eingebettetes iframe.

Rückgabewert

Typ
PerformanceScriptTiming
Beschreibung
Die Objekte stehen im Array PerformanceLongAnimationFrameTiming.scripts. toJSON() liefert eine JSON-taugliche Darstellung aller Eigenschaften, allerdings ohne window.

Beispiele

Skripte aus langen Animationsframes protokollieren

const observer = new PerformanceObserver((list) => {
  for (const frame of list.getEntries()) {
    for (const script of frame.scripts) {
      console.log(
        `${script.invokerType} | ${script.invoker} | ` +
        `${Math.round(script.duration)} ms | ${script.sourceURL}`
      );
    }
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
event-listener | BUTTON#buy.onclick | 182 ms | https://shop.example/app.js

Layout Thrashing und Kompilierzeit erkennen

new PerformanceObserver((list) => {
  list.getEntries()
    .flatMap((frame) => frame.scripts)
    .filter((s) => s.forcedStyleAndLayoutDuration > 10)
    .forEach((s) => {
      const compile = s.executionStart - s.startTime;
      console.warn('Erzwungenes Layout:', {
        funktion: s.sourceFunctionName || '(anonym)',
        quelle: `${s.sourceURL}:${s.sourceCharPosition}`,
        layoutMs: Math.round(s.forcedStyleAndLayoutDuration),
        kompilierMs: Math.round(compile)
      });
    });
}).observe({ type: 'long-animation-frame', buffered: true });
Erzwungenes Layout: { funktion: 'resizeCards', quelle: 'https://example.com/ui.js:4812', layoutMs: 37, kompilierMs: 2 }

Blockierzeit pro Drittanbieter-Domain summieren (RUM)

const blockzeitProHost = new Map();

const observer = new PerformanceObserver((list) => {
  for (const frame of list.getEntries()) {
    for (const script of frame.scripts) {
      if (!script.sourceURL) continue;
      const host = new URL(script.sourceURL).host;
      blockzeitProHost.set(host, (blockzeitProHost.get(host) ?? 0) + script.duration);
    }
  }
});

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

  addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      const daten = JSON.stringify(Object.fromEntries(blockzeitProHost));
      navigator.sendBeacon('/rum/loaf', daten);
    }
  });
}
Gesendet z. B.: {"example.com":240.5,"cdn.tracker.io":612.3}