Start · Sprachen · JavaScript · Referenz · Profiler.stop()

Profiler.stop()

Methode

Stoppt einen laufenden <code>Profiler</code> und gibt ein Promise zurück, das mit dem aufgezeichneten Profil (<code>ProfilerTrace</code>) erfüllt wird.

seit JavaScript JS Self-Profiling API (experimentell) – Chrome/Edg Kategorie: webapi

Signatur

Profiler.prototype.stop()

Beschreibung

stop() beendet die Stichprobenaufzeichnung eines Profilers, der mit new Profiler(options) gestartet wurde. Der Rückgabewert ist ein Promise, das mit einem ProfilerTrace-Objekt erfüllt wird. Darin steht, welche JavaScript-Funktionen während der Aufzeichnung auf dem Aufrufstapel lagen.

Die Methode gehört zur JS Self-Profiling API. Mit ihr kannst du die Leistung deines Codes direkt beim Nutzer messen, also in echten Sitzungen und nicht nur in den DevTools. Typische Einsätze: langsame Interaktionen analysieren, Startzeiten messen oder Profile gezielt an einen Analyse-Server schicken.

Das Profil ist platzsparend normalisiert und besteht aus vier Arrays:

  • resources: Skript-URLs
  • frames: Funktionsname, resourceId, line, column
  • stacks: frameId und parentId, bilden zusammen einen Baum
  • samples: timestamp und stackId

Um einen vollständigen Aufrufstapel zu erhalten, gehst du von stackId aus über die parentId-Kette nach oben.

Nach stop() ist die Profiler-Instanz verbraucht. Für eine neue Messung erzeugst du einen neuen Profiler. Die Eigenschaft stopped zeigt an, ob der Profiler schon beendet wurde.

Rückgabewert

Typ
Promise<ProfilerTrace>
Beschreibung
Promise, das mit einem ProfilerTrace-Objekt ({ resources, frames, stacks, samples }) erfüllt wird. Es wird mit einer InvalidStateError-DOMException abgelehnt, wenn der Profiler bereits gestoppt war.

Beispiele

Einfache Messung einer rechenintensiven Funktion

const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2));

const profileFib = async () => {
  const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10_000 });
  fib(30);
  const trace = await profiler.stop();
  console.log(`Samples: ${trace.samples.length}, Frames: ${trace.frames.length}`);
};

profileFib();
Samples: 42, Frames: 3

Profil einer Nutzerinteraktion an den Server senden

const button = document.querySelector('#export');

button.addEventListener('click', async () => {
  if (!('Profiler' in window)) return exportData();

  const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 5_000 });
  try {
    await exportData(); // die zu messende Arbeit
  } finally {
    const trace = await profiler.stop();
    navigator.sendBeacon('/api/profiles', JSON.stringify(trace));
  }
});

async function exportData() {
  const rows = Array.from({ length: 200_000 }, (_, i) => `${i};${Math.random()}`);
  return rows.join('\n');
}

Häufigste Funktionen aus dem Trace ermitteln

const topFunctions = (trace, limit = 5) => {
  const counts = new Map();
  for (const { stackId } of trace.samples) {
    if (stackId === undefined) continue; // Leerlauf-Sample
    const { frameId } = trace.stacks[stackId];
    const name = trace.frames[frameId].name || '(anonym)';
    counts.set(name, (counts.get(name) ?? 0) + 1);
  }
  return [...counts].sort((a, b) => b[1] - a[1]).slice(0, limit);
};

const run = async () => {
  const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10_000 });
  const data = Array.from({ length: 1e6 }, () => Math.random());
  data.sort((a, b) => a - b);
  const trace = await profiler.stop();
  console.table(topFunctions(trace));
};

run();
┌─────────┬──────────┬────┐ │ (index) │ 0 │ 1 │ ├─────────┼──────────┼────┤ │ 0 │ 'sort' │ 38 │ │ 1 │ 'run' │ 12 │ └─────────┴──────────┴────┘

Doppeltes Stoppen abfangen

const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 1_000 });
await profiler.stop();
console.log(profiler.stopped);

try {
  await profiler.stop();
} catch (err) {
  console.log(err.name);
}
true InvalidStateError

// Wichtig · Fallstricke

  • Header erforderlich: Das HTML-Dokument muss mit dem HTTP-Header Document-Policy: js-profiling ausgeliefert werden. Fehlt der Header, wirft schon new Profiler() einen NotAllowedError.
  • Kompatibilität: Die API ist experimentell und nur in Chromium-Browsern verfügbar. Vorher immer mit 'Profiler' in window prüfen.
  • Nur einmal stoppen: Ein zweiter Aufruf von stop() lehnt das Promise mit InvalidStateError ab. Eine Instanz kann nicht neu gestartet werden.
  • Puffergrenze: Ist maxBufferSize erreicht, wird das Ereignis samplebufferfull ausgelöst und es kommen keine weiteren Samples hinzu. In diesem Fall solltest du stop() zeitnah aufrufen, um die bisherigen Daten zu sichern.
  • Genauigkeit: Das tatsächliche Intervall zwischen den Samples kann größer sein als sampleInterval, denn der Browser rundet auf seine eigene Mindestauflösung (oft 10 ms, unter Windows z. B. 16 ms). Sehr kurze Code-Abschnitte erscheinen deshalb eventuell gar nicht im Profil.
  • Datenschutz und Leistung: Frames aus fremden Skripten ohne CORS-Freigabe werden weggelassen. Profiling kostet selbst etwas Leistung. Zeichne deshalb in Produktion nur bei einem kleinen Teil der Nutzer auf (Sampling).