Signatur
Beschreibung
Die schreibgeschützte Eigenschaft executionStart gehört zum Interface PerformanceScriptTiming der Long Animation Frames API (LoAF). Sie enthält einen DOMHighResTimeStamp in Millisekunden, gemessen ab performance.timeOrigin. Der Wert gibt an, wann die Kompilierung des Skripts abgeschlossen war und die eigentliche Ausführung begonnen hat.
Zusammen mit der geerbten Eigenschaft startTime lässt sich ein Skript-Eintrag in zwei Phasen aufteilen:
- Kompilierungs- bzw. Vorbereitungszeit:
executionStart - startTime - Reine Ausführungszeit:
(startTime + duration) - executionStart
An PerformanceScriptTiming-Objekte kommt man nicht direkt heran. Sie stecken im Array scripts eines PerformanceLongAnimationFrameTiming-Eintrags, den ein PerformanceObserver mit dem Typ "long-animation-frame" liefert. Die Eigenschaft hilft bei der Frage, ob ein langer Frame von großen, teuer zu parsenden Skript-Bundles kommt oder von aufwendiger Laufzeitlogik. Im ersten Fall helfen Code-Splitting und kleinere Bundles, im zweiten eher Aufteilen der Arbeit mit scheduler.yield() oder setTimeout.
Rückgabewert
performance.timeOrigin, zu dem die Skriptausführung nach der Kompilierung begann. Der Wert ist immer größer oder gleich startTime des Eintrags.Beispiele
Kompilierungs- und Ausführungszeit langer Frames messen
const observer = new PerformanceObserver((list) => {
for (const frame of list.getEntries()) {
for (const script of frame.scripts) {
const compile = script.executionStart - script.startTime;
const execute = script.startTime + script.duration - script.executionStart;
console.log(
`${script.invoker}: Kompilierung ${compile.toFixed(1)} ms, ` +
`Ausführung ${execute.toFixed(1)} ms`
);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Skripte mit hohem Kompilierungsanteil finden
const findeTeureBundles = (schwelleMs = 20) => {
const frames = performance.getEntriesByType('long-animation-frame');
return frames
.flatMap((frame) => frame.scripts)
.map((s) => ({
quelle: s.sourceURL || s.invoker,
kompilierung: Math.round(s.executionStart - s.startTime)
}))
.filter((s) => s.kompilierung >= schwelleMs)
.sort((a, b) => b.kompilierung - a.kompilierung);
};
console.table(findeTeureBundles());
Mit Feature-Erkennung an ein RUM-Backend senden
const unterstuetzt = PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame');
if (unterstuetzt) {
new PerformanceObserver((list) => {
const daten = list.getEntries().flatMap((frame) =>
frame.scripts.map((s) => ({
url: s.sourceURL,
funktion: s.sourceFunctionName,
executionStart: Math.round(s.executionStart),
kompilierungMs: Math.round(s.executionStart - s.startTime)
}))
);
if (daten.length) {
navigator.sendBeacon('/rum/loaf', JSON.stringify(daten));
}
}).observe({ type: 'long-animation-frame', buffered: true });
} else {
console.info('Long Animation Frames API wird nicht unterstützt.');
}
// Wichtig · Fallstricke
- Browser-Unterstützung: Die API gibt es bisher nur in Chromium-basierten Browsern (ab Version 123). Firefox und Safari unterstützen sie nicht. Vorher per
PerformanceObserver.supportedEntryTypesprüfen, ob sie verfügbar ist. - Nur lange Skripte: In
scriptslanden nur Skripte, die länger als ca. 5 ms laufen und zu einem Frame von über 50 ms gehören. Kurze Skripte tauchen nicht auf. - Relativ zu timeOrigin: Der Wert ist keine Unix-Zeit. Für eine absolute Zeit muss
performance.timeOrigindazugerechnet werden. - Kompilierung nicht immer sichtbar: Bei Callbacks wie Event-Listenern oder Promise-Reaktionen ist der Code meist schon kompiliert.
executionStartliegt dann sehr nah anstartTime, der Unterschied sagt wenig aus. - Genauigkeit: Zum Schutz vor Timing-Angriffen runden Browser die Zeitstempel ab. Ohne Cross-Origin-Isolation ist die Auflösung gröber.
- Datenschutz: Bei fremden (cross-origin) Frames oder Skripten gibt der Browser teilweise weniger Informationen heraus. Das kann z. B. die Zuordnung über
windowAttributionbetreffen.