Tarayıcıda bir işlemin ne kadar sürdüğünü ölçmenin doğru yolu Performance API'dir: kısa ölçümler için performance.now(), adlandırılmış ölçümler için performance.mark() ve performance.measure(), tarayıcının kendi kaydettiği sayfa ve kaynak yükleme süreleri için de PerformanceObserver kullanılır. Bu değerler Date.now() gibi sistem saatine bağlı değildir; geriye gitmeyen, milisaniyenin altında çözünürlüklü bir saatten gelir.
Bu yazıda performans zaman çizelgesini, now() ile Date.now() farkını, çözünürlüğün neden düşürüldüğünü, User Timing ile kendi işaretlerinizi, measure() metodunun bütün imzalarını, gözlemcilerle veri toplamayı, gezinme ve kaynak zamanlamalarını ve Server-Timing başlığını anlatıyoruz. Tarayıcıdan bağımsız kısımları Node.js 24 ile çalıştırıp doğruladık. LCP, INP ve CLS gibi Core Web Vitals metrikleri bu yazının değil, Core Web Vitals rehberi yazısının konusudur. Serinin tamamı için Web API öğrenme rehberi sayfasına bakabilirsiniz.
Performans zaman çizelgesi ve PerformanceEntry

MDN Performance API'yi tek bir arayüz değil, web uygulamalarının performansını ölçmeye yarayan bir standartlar grubu olarak tanımlar. Ortak nokta performans zaman çizelgesidir: tarayıcının yüksek çözünürlüklü zaman damgalarıyla doldurduğu ve geliştirici araçlarında da görebildiğiniz bir kayıt listesi. Her ölçüm bir PerformanceEntry nesnesidir ve dört temel özelliği vardır: name, entryType, startTime ve duration. Gezinme, kaynak, olay gibi bütün özel kayıt türleri bu arayüzü genişletir.
Kayıtların çoğu siz hiçbir şey yapmadan tarayıcı tarafından eklenir; örneğin her yüklenen görsel veya betik için bir kaynak kaydı oluşur. Siz de PerformanceMark ve PerformanceMeasure ile kendi kayıtlarınızı ekleyebilirsiniz. Ana Performance arayüzü hem Window hem de Worker kapsamlarında bulunur; yani bir worker içindeki işin süresini de aynı yöntemlerle ölçebilirsiniz.
| entryType | Arayüz | Kim ekler |
|---|---|---|
| mark | PerformanceMark | Siz, performance.mark() ile |
| measure | PerformanceMeasure | Siz, performance.measure() ile |
| navigation | PerformanceNavigationTiming | Tarayıcı, belge yüklenince |
| resource | PerformanceResourceTiming | Tarayıcı, her kaynak için |
| paint | PerformancePaintTiming | Tarayıcı, ilk boyama anlarında |
| event | PerformanceEventTiming | Tarayıcı, eşiği aşan olaylar için |
| longtask | PerformanceLongTaskTiming | Tarayıcı, uzun görevler için |
performance.now() ile Date.now() farkı
performance.now() milisaniye cinsinden bir DOMHighResTimeStamp döndürür, ama bu değer 1970 tarihinden değil performance.timeOrigin anından itibaren sayılır. Sayfada zaman başlangıcı gezinmenin başladığı andır; Worker ve ServiceWorker bağlamlarında ise worker'ın çalışmaya başladığı andır. Date.now() ise Unix başlangıcından bu yana geçen milisaniyeyi verir ve sistem saatine bağlıdır.
| performance.now() | Date.now() | |
|---|---|---|
| Çözünürlük | Milisaniyenin altı | Milisaniye |
| Başlangıç noktası | performance.timeOrigin | Unix başlangıcı (1 Ocak 1970, UTC) |
| Saat ayarlamalarından etkilenir mi | Hayır | Evet |
| Hep artar mı (monoton) | Evet | Hayır |
Bu tablonun pratik anlamı şudur: işletim sistemleri saati düzenli olarak eşitler ve Date değerleri bu ayarlamalarla birkaç milisaniye ileri ya da geri oynayabilir. Bir döngünün süresini Date.now() ile ölçerseniz araya giren bir saat ayarı sonucu bozabilir, hatta negatif süre görebilirsiniz. performance.now() monotondur, yani bir sonraki okuma her zaman öncekine eşit veya ondan büyüktür. MDN performans ölçümü, kare hızı hesabı ve animasyon döngüleri için bu yüzden performance.now() öneriyor; animasyon döngüsünde zaman damgasının nasıl kullanıldığı requestAnimationFrame yazısında anlatılıyor.
const t0 = performance.now();
let toplam = 0;
for (let i = 0; i < 1e6; i++) toplam += i;
const t1 = performance.now();
console.log(typeof t0); // number
console.log(t1 >= t0); // true
console.log(toplam); // 499999500000
console.log(`Döngü ${(t1 - t0).toFixed(2)} ms sürdü.`);Son satırın değeri cihaza ve ana göre değiştiği için çıktı olarak göstermedik; yalnızca değişmeyen sonuçları yazdık. Gerçek dünyadaki zamanı elde etmek isterseniz MDN'nin önerdiği formül performance.timeOrigin + performance.now() olur. Eski performance.timing.navigationStart tabanlı hesap saat değişikliği riski taşıdığı için yerini bu formüle bırakmıştır.
Çözünürlük neden düşürülür?
MDN, zamanlama saldırılarına ve parmak izi çıkarmaya karşı koruma için DOMHighResTimeStamp değerlerinin sitenin izolasyon durumuna göre kabalaştırıldığını belirtir: kökenler arası izole (cross-origin isolated) bağlamlarda çözünürlük 5 mikrosaniye, izole olmayan bağlamlarda 100 mikrosaniyedir. Sayfanızın izole olup olmadığını crossOriginIsolated özelliğiyle öğrenebilirsiniz. İzolasyon için MDN şu iki başlığı gösterir:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpBu başlıklar sayfanızın gömdüğü bütün kaynakları etkilediği için yalnızca daha hassas zamanlama elde etmek amacıyla açılmamalı; kökenler arası kaynakların davranışını CORS rehberi yazısında okuyabilirsiniz. MDN bir uç duruma daha dikkat çeker: spesifikasyon performance.now() değerinin işletim sistemi uykudayken de ilerlemesini ister, ama MDN'ye göre bunu yalnızca Windows'taki tarayıcılar yapar. Uykuya girilebilecek kadar uzun işlemleri ölçerken bu farkı hesaba katın.
User Timing: mark() ve measure()
Tarayıcı, uygulamanızın içinde neler olduğunu bilemez: kullanıcının giriş yaptığı, sepete ürün eklediği veya bir raporun çizildiği anları siz işaretlemelisiniz. User Timing bu iş için vardır. performance.now() değerlerini değişkenlerde tutmaya göre üstünlüğü, işaretlerin bir adı olması ve geliştirici araçlarının performans panelinde görünmesidir; ayrıca PerformanceObserver ile de yakalanabilirler.
performance.mark(ad, secenekler) zaman çizelgesine adlandırılmış bir an ekler ve oluşan PerformanceMark nesnesini döndürür. Seçenekler nesnesinde detail ile yapılandırılmış klonlanabilir herhangi bir ek veri, startTime ile de performance.now() yerine kullanılacak bir zaman verebilirsiniz. Aynı adla birden fazla işaret oluşturmak serbesttir. Ad, kullanımdan kalkmış PerformanceTiming arayüzünün özellik adlarından biri olamaz; MDN bu durumda SyntaxError fırlatıldığını belirtir.
performance.measure() iki an arasındaki süreyi ölçen bir PerformanceMeasure oluşturur. Aşağıdaki örnekte sabit startTime değerleri verdik, böylece çıktı her çalıştırmada aynı oluyor:
performance.mark("sepet-acildi", { startTime: 100 });
performance.mark("odeme-bitti", { startTime: 342.5, detail: { adim: 3 } });
const olcum = performance.measure("odeme-suresi", "sepet-acildi", "odeme-bitti");
console.log(olcum.entryType); // measure
console.log(olcum.startTime); // 100
console.log(olcum.duration); // 242.5
const isaret = performance.getEntriesByName("odeme-bitti", "mark")[0];
console.log(JSON.stringify(isaret.detail)); // {"adim":3}
// İşaret yerine zaman ve süre vermek
const o2 = performance.measure("sabit", { start: 50, duration: 25 });
console.log(o2.startTime, o2.duration); // 50 25
const o3 = performance.measure("sondan", { end: 200, duration: 30 });
console.log(o3.startTime, o3.duration); // 170 30measure() imzaları ve kuralları
measure() beş farklı biçimde çağrılabilir: yalnızca ad; ad ve başlangıç işareti; ad, başlangıç ve bitiş işareti; ad ve seçenekler nesnesi; ad, seçenekler ve bitiş işareti. Sadece ad verirseniz başlangıç 0, bitiş o anki performance.now() değeri olur. Yalnızca bitiş işareti vermek istiyorsanız MDN boş bir seçenekler nesnesi geçmenizi söyler: performance.measure("olcum", {}, "bitis"). Seçenekler nesnesinde start, end ve duration alanları bulunur; start ve end bir zaman damgası veya işaret adı olabilir.
Belirsizlik doğuran birleşimler TypeError fırlatır: hem bitiş işareti hem seçenekler nesnesi vermek, duration verip start ve end değerlerinden hiçbirini vermemek veya üçünü birden vermek. Var olmayan bir işaret adı ise SyntaxError türünde bir DOMException üretir. mark() tarafında ise negatif bir startTime MDN'ye göre TypeError verir. Aşağıdaki üç durumu çalıştırarak doğruladık. Yalnızca duration verilen durumu örneğe koymadık, çünkü denediğimiz Node.js sürümü bu çağrıda hata fırlatmadı; tarayıcıdaki davranış için MDN'deki kurala güvenin:
try {
performance.measure("hatali", "olmayan-isaret");
} catch (hata) {
console.log(hata.name); // SyntaxError
}
try {
performance.measure("belirsiz", { start: 1, end: 2, duration: 1 });
} catch (hata) {
console.log(hata.name); // TypeError
}
try {
performance.mark("eksi", { startTime: -1 });
} catch (hata) {
console.log(hata.name); // TypeError
}
performance.mark("tekrar");
performance.mark("tekrar");
console.log(performance.getEntriesByName("tekrar").length); // 2
performance.clearMarks("tekrar");
console.log(performance.getEntriesByName("tekrar").length); // 0Son dört satır temizlik metotlarını gösteriyor: clearMarks() ve clearMeasures() parametresiz çağrılınca hepsini, adla çağrılınca yalnızca o adı taşıyan kayıtları siler. MDN, işaret ve ölçüm türleri için ara bellek sınırının sonsuz olduğunu belirtir; bu yüzden tek sayfalık uzun ömürlü uygulamalarda düzenli temizlik yapmak belleği korur.
PerformanceObserver ile kayıtları toplamak
Kayıtlara iki yoldan ulaşırsınız: performance.getEntries(), getEntriesByType() ve getEntriesByName() ile o ana kadar oluşanları bir kerede alırsınız ya da bir PerformanceObserver ile yenilerini geldikçe dinlersiniz. MDN gözlemciyi tercih edilen yol olarak sunar ve üç neden sayar: getEntries* metotları her çağrıda zaman çizelgesinin başından itibaren bütün kayıtları döndürdüğü için daha önce gördüklerinizi ayıklamanız gerekir; gözlemci bildirimleri eşzamansız gelir, böylece tarayıcı bunları boş zamanda iletebilir; bazı kayıt türleri ise getEntries* ile hiç alınamaz.
const gozlemci = new PerformanceObserver((liste) => {
for (const kayit of liste.getEntries()) {
console.log(`${kayit.entryType}: ${kayit.name}`);
}
});
gozlemci.observe({ entryTypes: ["mark", "measure"] });
performance.mark("a");
performance.mark("b");
performance.measure("a-b", "a", "b");observe() metodunun seçenekleri birbirini dışlayan iki grup oluşturur. entryTypes bir dizi alır ve birden fazla türü aynı anda izler, ama type, buffered ve durationThreshold ile birlikte kullanılamaz. type tek bir tür alır ve buffered: true ile gözlemci oluşturulmadan önce kaydedilmiş girdileri de getirebilir. MDN, buffered veya durationThreshold gerektiğinde type kullanmanız gerektiğini, aksi halde birden fazla tür toplamanın çalışmayacağını vurgular. Tanınmayan türler yok sayılır; hiç geçerli tür yoksa observe() etkisiz kalır.
Tarayıcının desteklediği türleri PerformanceObserver.supportedEntryTypes ile öğrenebilirsiniz. İşiniz bitince disconnect() çağırın; bekleyen kayıtları beklemeden almak için takeRecords() vardır. Geri çağrının üçüncü parametresi olan droppedEntriesCount, ara bellek dolduğu için kaybolan kayıt sayısını verir.
Gezinme ve kaynak zamanlamaları
Belgenin kendisi için tarayıcı bir PerformanceNavigationTiming kaydı oluşturur. Yalnızca geçerli belge dahil edildiği için genellikle tek bir kayıt vardır; startTime değeri her zaman 0'dır. Bu kayıt kaynak zamanlamasını da miras aldığı için belgenin indirilmesine ait bütün zaman damgalarını içerir; ek olarak domInteractive, domContentLoadedEventStart, domContentLoadedEventEnd, domComplete, loadEventStart ve loadEventEnd gibi belge aşamalarını taşır. type özelliği gezinmenin türünü, redirectCount yönlendirme sayısını verir. Bu olayların anlamı DOMContentLoaded ve load yazısında anlatılıyor. MDN'nin DOMContentLoaded işleyicisinin süresini ölçen örneği şöyledir:
const gozlemci = new PerformanceObserver((liste) => {
liste.getEntries().forEach((kayit) => {
const sure = kayit.domContentLoadedEventEnd - kayit.domContentLoadedEventStart;
console.log(`${kayit.name}: DOMContentLoaded işleme süresi: ${sure} ms`);
});
});
gozlemci.observe({ type: "navigation", buffered: true });Sayfadaki her görsel, betik, stil dosyası ve fetch() çağrısı için de bir PerformanceResourceTiming kaydı oluşur. Bu kayıtlar DNS, bağlantı, istek ve yanıt aşamalarının zaman damgalarının yanında üç boyut bilgisi verir: transferSize başlıklar dahil aktarılan bayt, encodedBodySize içerik kodlaması çözülmeden önceki gövde, decodedBodySize çözüldükten sonraki gövde boyutudur. Kaynak kayıtlarının ara belleği varsayılan olarak 250 kayıtla sınırlıdır; daha fazlası için performance.setResourceTimingBufferSize() çağırır, dolduğunda resourcetimingbufferfull olayını dinlersiniz.
Kökenler arası kaynaklarda dikkat edilecek bir nokta var: MDN, birçok zamanlama özelliğinin bu kaynaklarda 0 veya boş dize döndürdüğünü, ayrıntıların görünmesi için kaynağı sunan tarafın Timing-Allow-Origin yanıt başlığını göndermesi gerektiğini söyler. CDN'den gelen dosyaların süresi sıfır görünüyorsa nedeni budur.
Server-Timing ile sunucu sürelerini tarayıcıda görmek
Sunucunuz yanıtta Server-Timing başlığını gönderirse, veritabanı sorgusu veya önbellek okuması gibi arka uç süreleri hem geliştirici araçlarında hem de JavaScript tarafında görünür. Ayrı bir kayıt türü yoktur; değerler gezinme ve kaynak kayıtlarının serverTiming dizisinde PerformanceServerTiming nesneleri olarak gelir. MDN, sunucu ile istemci arasında saat eşitlemesi olmadığını, bu yüzden sunucunun gönderdiği zaman damgalarının istemci zaman çizelgesiyle doğrudan karşılaştırılamayacağını hatırlatır. Örnek başlık ve MDN'nin gösterdiği çıktılar:
Server-Timing: cache;desc="Cache Read";dur=23.2,db;dur=53,app;dur=47.2const gozlemci = new PerformanceObserver((liste) => {
liste.getEntries().forEach((kayit) => {
kayit.serverTiming.forEach((s) => {
console.log(`${s.name} (${s.description}) duration: ${s.duration}`);
// Logs "cache (Cache Read) duration: 23.2"
// Logs "db () duration: 53"
// Logs "app () duration: 47.2"
});
});
});
["navigation", "resource"].forEach((type) => gozlemci.observe({ type, buffered: true }));Toplanan kayıtları bir analiz uç noktasına göndermek isterseniz her kaydın toJSON() metodu vardır ve JSON.stringify(kayit) bunu otomatik çağırır. Sayfa kapanırken veriyi kaybetmeden göndermek için sendBeacon ve sayfa yaşam döngüsü yazısına bakın. HTTP başlıklarının genel yapısı HTTP istek ve yanıt yapısı yazısında.
Sık yapılan hatalar

- Süre ölçmek için
Date.now()kullanmak. Saat ayarlamalarından etkilenir ve monoton değildir. - Tek bir ölçüme güvenmek. Süreler cihaza, yüke ve çözünürlük kabalaştırmasına göre değişir; çok sayıda ölçümün dağılımına bakın.
getEntries()metodunu döngüde tekrar tekrar çağırmak. Her çağrı bütün kayıtları yeniden döndürür;PerformanceObserverkullanın.entryTypesilebufferedbirlikte vermek. Bu seçenekler birbirini dışlar; geçmiş kayıtlar içintypeile ayrı ayrıobserve()çağırın.- Var olmayan işaret adıyla
measure()çağırmak.SyntaxErroralırsınız; işaretin gerçekten oluştuğundan emin olun. - Kökenler arası kaynakların süresini sıfır görüp hatalı sanmak.
Timing-Allow-Originbaşlığı yoksa ayrıntılar gizlenir. - İşaretleri hiç temizlememek. İşaret ve ölçüm ara belleği sınırsızdır; uzun ömürlü sayfalarda
clearMarks()veclearMeasures()kullanın.
Sık Sorulan Sorular
performance.now() değeri neden küçük bir sayı?
Çünkü 1970'ten değil, sayfada gezinmenin başladığı andan itibaren sayılır. Gerçek zamana çevirmek için performance.timeOrigin değerini ekleyin.
İşaretlerim geliştirici araçlarında görünür mü?
MDN, User Timing ile oluşturulan işaretlerin tarayıcıların performans panellerinde gösterilebildiğini belirtir. MDN bunu, Date.now() veya performance.now() değerlerini değişkenlerde tutmaya göre User Timing'in temel üstünlüğü olarak sayar.
Performance API Web Worker içinde çalışır mı?
Evet. Performance arayüzü Window ve Worker kapsamlarında bulunur. Worker'ın zaman başlangıcı farklı olduğu için iki bağlamın zamanlarını karşılaştırırken timeOrigin ile hizalamanız gerekir; worker kullanımı Web Workers yazısında.
Core Web Vitals değerlerini bu API ile mi ölçüyorum?
Bu metriklerin ham verileri largest-contentful-paint, layout-shift ve event gibi kayıt türleriyle zaman çizelgesine düşer ve PerformanceObserver ile okunur. Metriklerin anlamı ve iyileştirilmesi Core Web Vitals rehberi yazısında anlatılıyor.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · requestAnimationFrame · Sayfa yaşam döngüsü · Core Web Vitals · Web Workers · HTTP istek ve yanıt yapısı