Performance API
Measuring real-world performance — performance.now(), marks, measures, PerformanceObserver, Core Web Vitals, and the Navigation Timing API.
What it is, in plain English: The Performance API provides high-resolution timing and performance measurement tools. performance.now() gives sub-millisecond timestamps. performance.mark() and performance.measure() create named timing points visible in DevTools. The PerformanceObserver watches for performance entries in real time. Core Web Vitals (LCP, FID, CLS) are the key user experience metrics measured with the Performance API.
Where this bites you
Timing an async function without await measures how long it took to start the work, not to do it. The call returns a Promise immediately, the second performance.now() runs straight away, and a 300 ms fetch reports well under a millisecond. It's an easy way to "prove" that slow code is fast.
async function loadReport() {
await new Promise(r => setTimeout(r, 300)); // stands in for a 300 ms fetch
return "report";
}
const start = performance.now();
loadReport(); // no await
console.log(`Took ${(performance.now() - start).toFixed(1)}ms`);
// Took 0.6ms (Chromium; 0.1ms in Node)Measuring how fast your code runs
performance.now() — precise timing
// Measures real elapsed time, sub-millisecond precision:
const start = performance.now();
doSomethingExpensive();
const elapsed = performance.now() - start;
console.log(`Took ${elapsed.toFixed(3)}ms`);
// Compare two approaches:
const t1 = performance.now();
approach1(data);
const t2 = performance.now();
approach2(data);
const t3 = performance.now();
console.log(`Approach 1: ${(t2-t1).toFixed(2)}ms`);
console.log(`Approach 2: ${(t3-t2).toFixed(2)}ms`);The examples above time synchronous functions, which is why they work as written. For anything that returns a Promise, the end mark has to come after await, as in the marks-and-measures example below:
async function loadReport() {
await new Promise(r => setTimeout(r, 300));
return "report";
}
async function main() {
const start = performance.now();
await loadReport(); // wait for the work to finish
console.log(`Took ${(performance.now() - start).toFixed(1)}ms`);
}
main();
// Took 300.1ms (exact ms vary run to run)Named marks and measures
// Create named timing points visible in DevTools Performance tab:
performance.mark("data:fetch:start");
const data = await fetchData();
performance.mark("data:fetch:end");
performance.mark("data:render:start");
renderData(data);
performance.mark("data:render:end");
// Create measures between marks:
performance.measure("fetch time", "data:fetch:start", "data:fetch:end");
performance.measure("render time", "data:render:start", "data:render:end");
// Read the measurements:
performance.getEntriesByType("measure").forEach(m => {
console.log(`${m.name}: ${m.duration.toFixed(2)}ms`);
});
Try It Yourself
Official Sources
- MDN Web Docs — Performance API
- MDN Web Docs — PerformanceObserver
- web.dev — Core Web Vitals
- MDN Web Docs — Navigation Timing API
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.