---
title: 'Zamanlama ve Çizim Döngüsü'
source: 'https://academia.sh/tr/kurslar/tarayici-platformu/zamanlama-ve-cizim-dongusu'
course: 'Tarayıcı ve Web Platformu'
language: tr
updated: '2026-08-17T18:09:11+00:00'
license: 'CC BY-SA 4.0'
---

# Zamanlama ve Çizim Döngüsü

Tarayıcının kare hazırlama sırası ve güncellemenin bu sıraya yerleştirilmesi; kare bütçesi, sabit gecikmeli zamanlayıcının kaydırdığı hiza, kare geri çağırımının kuralları, okuma ile yazmanın ayrılması ve uzun işin bölünmesi.

Gözlemciler ölçümün ne zaman alınacağını çözer, değişikliğin ne zaman uygulanacağını açık
bırakır. Ölçüm listesi kaydırılırken başlığın konumunu güncelleyen ya da bir çubuğu büyüten
kod, bunu rastgele bir anda yaparsa tarayıcının çizim düzenine ters düşer.

Tarayıcı ekranı sürekli değil, **kare kare** yeniler. Her karede belirli bir sırayı izler
ve bu sıra tanımlıdır. Güncellemeyi bu sıraya yerleştirmek, hareketin düzgün görünmesinin
tek yoludur.

## Karenin Anatomisi

Bir kare şu aşamalardan geçer: birikmiş girdi olayları işlenir, süresi dolan zamanlayıcılar
çalışır, kare geri çağırımları çağrılır, biçem hesaplanır, yerleşim yapılır, boyama yapılır
ve katmanlar birleştirilir.

Bu sıradan iki sonuç çıkar. Birincisi, kare geri çağırımları **biçem ve yerleşimden önce**
çalışır; orada yapılan değişiklik aynı karede ekrana yansır. İkincisi, bir karede yapılan
bütün işin toplamı, karenin süresini aşmamalıdır.

Karenin süresi ekranın yenileme hızına bağlıdır ve her cihazda aynı değildir. Saniyede 60
kare varsayımıyla bir kare 16,67 milisaniye sürer; daha hızlı ekranlarda bu süre kısalır.
Bu nedenle yazılan kod belirli bir süreyi varsaymak yerine, tarayıcının verdiği zaman
damgasına dayanmalıdır.

## Zamanlayıcı ile Kare Hizası

Yaygın bir yaklaşım, güncellemeyi sabit gecikmeli bir zamanlayıcıya bağlamaktır. Bu
yaklaşımın sorunu, gecikmenin **işin bitişinden** sayılmasıdır: her turda işin süresi
kadar bir kayma birikir.

```js
// kare.mjs — sabit gecikmeli zamanlayici ile kare hizasindaki geri cagirimin karsilastirmasi
const KARE = 1000 / 60;              // saniyede 60 kare varsayimiyla kare suresi (ms)
const IS = 4;                        // her guncellemenin isledigi sure (ms)
const ADIM = 8;

const kareNo = (t) => Math.floor(t / KARE);

// Zamanlayici: her tur, isin bitisinden 16 ms sonrasina yeniden kurulur.
let t = 0;
const zamanlayici = [];
for (let i = 0; i < ADIM; i += 1) {
  zamanlayici.push(t);
  t += IS + 16;
}

// Kare hizasi: geri cagirim kare basinda calisir, is suresi bir sonraki hedefi kaydirmaz.
const hizali = Array.from({ length: ADIM }, (_, i) => i * KARE);

const sayim = (anlar) => {
  const kutu = new Map();
  for (const an of anlar) kutu.set(kareNo(an), (kutu.get(kareNo(an)) ?? 0) + 1);
  return kutu;
};

console.log("adim  zamanlayici(ms)  kare   kare hizasi(ms)  kare");
for (let i = 0; i < ADIM; i += 1)
  console.log(
    String(i + 1).padStart(4),
    zamanlayici[i].toFixed(2).padStart(15), String(kareNo(zamanlayici[i])).padStart(6),
    hizali[i].toFixed(2).padStart(17), String(kareNo(hizali[i])).padStart(6),
  );

const bos = (anlar) => {
  const kutu = sayim(anlar);
  const son = kareNo(anlar[anlar.length - 1]);
  let n = 0;
  for (let k = 0; k <= son; k += 1) if ((kutu.get(k) ?? 0) === 0) n += 1;
  return { son, bos: n };
};
for (const [ad, anlar] of [["zamanlayici", zamanlayici], ["kare hizasi", hizali]]) {
  const { son, bos: n } = bos(anlar);
  console.log(`${ad.padEnd(11)}: ${ADIM} guncelleme, kare 0..${son} araligina yayildi, bos kare: ${n}`);
}
console.log("kare butcesi:", KARE.toFixed(2), "ms | is:", IS, "ms | kalan:", (KARE - IS).toFixed(2), "ms");
```

```
adim  zamanlayici(ms)  kare   kare hizasi(ms)  kare
   1            0.00      0              0.00      0
   2           20.00      1             16.67      1
   3           40.00      2             33.33      2
   4           60.00      3             50.00      3
   5           80.00      4             66.67      4
   6          100.00      6             83.33      5
   7          120.00      7            100.00      6
   8          140.00      8            116.67      7
zamanlayici: 8 guncelleme, kare 0..8 araligina yayildi, bos kare: 1
kare hizasi: 8 guncelleme, kare 0..7 araligina yayildi, bos kare: 0
kare butcesi: 16.67 ms | is: 4 ms | kalan: 12.67 ms
```

Kayma altıncı adımda görünür hâle gelir: beşinci kare hiçbir güncelleme almaz, altıncı kare
bir sonraki güncellemeyi alır. Kullanıcının gördüğü şey donma değil, düzensizliktir —
hareket bir an duraksar. Aynı sayıda güncelleme kare hizasında yapıldığında boş kare kalmaz.

Sabit gecikmeli zamanlayıcının ikinci sorunu, karesi olmayan anlarda da çalışmasıdır. Sekme
arka plana alındığında ekran yenilenmez; zamanlayıcı yine de tetiklenir ve hiç görünmeyecek
kareler hesaplanır. Kare geri çağırımı bu durumda çağrılmaz ve iş kendiliğinden durur.

## Kare Geri Çağırımının Kuralları

Kare geri çağırımı **tek seferliktir**: bir sonraki kare için yeniden kaydolmak gerekir.
Süregelen bir hareket, geri çağırımın içinden kendini yeniden kaydeder; durdurmak, kaydı
yenilememek ya da bekleyen kaydı iptal etmektir.

Geri çağırım, karenin zaman damgasını argüman olarak alır. Hareketin ilerlemesi bu damgadan
hesaplanır: "her çağrıda bir piksel" yerine "geçen süreye göre şu kadar piksel". Birinci
biçim, kare hızı değiştiğinde hareketin hızını da değiştirir; ikinci biçim cihazdan
bağımsızdır. Aynı damga o karedeki bütün geri çağırımlara verilir, böylece aynı karede
güncellenen ögeler birbiriyle tutarlı kalır.

Kullanıcının hareket tercihi ayrı bir kuraldır. İşletim sisteminde hareketin azaltılması
istenmişse, bu tercih biçem tarafında sorgulanabilir ve program tarafında da okunabilir;
tercih varsa hareket kaldırılır ya da anlık geçişe indirilir.

## Okuma ve Yazmayı Ayırmak

Kare içinde yapılan işin ucuz kalması için ikinci bir kural gerekir. Bir ögenin ölçüsünü
sorgulamak, bekleyen bütün yazmaların o anda işlenmesini zorlar; Gözlemciler dersinde
anılan zorlama budur. Ölçüm ile yazmanın sırayla yapılması bu zorlamayı her turda tetikler.

```js
// okuma.mjs — okuma ile yazmanin siralanmasinin zorladigi yerlesim sayisi
function kurulum() {
  return {
    cubuklar: [40, 55, 30, 72, 61].map((g, i) => ({ ad: `T-0${i + 1}`, genislik: g })),
    kirli: false,          // bekleyen yazma var mi
    yerlesim: 0,           // zorlanmis yerlesim hesabi sayisi
  };
}
const oku = (d, cubuk) => {
  if (d.kirli) { d.yerlesim += 1; d.kirli = false; }   // olcum, bekleyen yazmalari isletir
  return cubuk.genislik;
};
const yaz = (d, cubuk, deger) => { cubuk.genislik = deger; d.kirli = true; };

// Birinci yol: her cubukta once oku sonra yaz.
const a = kurulum();
for (const cubuk of a.cubuklar) yaz(a, cubuk, oku(a, cubuk) + 10);
console.log("araya girmis :", a.yerlesim, "zorlanmis yerlesim |",
  a.cubuklar.map((c) => c.genislik).join(" "));

// Ikinci yol: once butun olcumler, sonra butun yazmalar.
const b = kurulum();
const olculen = b.cubuklar.map((cubuk) => oku(b, cubuk));
b.cubuklar.forEach((cubuk, i) => yaz(b, cubuk, olculen[i] + 10));
console.log("ayrilmis     :", b.yerlesim, "zorlanmis yerlesim |",
  b.cubuklar.map((c) => c.genislik).join(" "));
console.log("sonuclar ayni:", JSON.stringify(a.cubuklar) === JSON.stringify(b.cubuklar));
```

```
araya girmis : 4 zorlanmis yerlesim | 50 65 40 82 71
ayrilmis     : 0 zorlanmis yerlesim | 50 65 40 82 71
sonuclar ayni: true
```

İki yol aynı sonucu üretir; maliyetleri farklıdır. Ayrılmış sırada ölçümler tek seferde
alınır, yazmalar birikir ve yerleşim karenin kendi aşamasında bir kez yapılır.

Buradaki sayı yine bir başarım ölçümü değil, **çağrı sayımıdır**; gerçek maliyet belgeye ve
biçeme bağlıdır. Sabit olan şey, tetiklenen hesap sayısının dörtten sıfıra inmesidir.
Kural, öge sayısı arttıkça daha da belirginleşir: ayrılmış sıra sabit sayıda hesap üretir,
araya girmiş sıra öge sayısıyla doğru orantılı.

## Uzun İşi Bölmek

Kare bütçesini aşan tek bir iş, o karede yapılacak her şeyi bekletir: girdi işlenmez,
hareket ilerlemez. Bin ölçümü tek turda süzen bir işlev bunu yapar.

Çözüm işi bölmektir. Parça işlenir, denetim çalışma zamanına geri verilir, sonraki parça
bir sonraki turda işlenir. Böylece girdi olayları aradaki boşluklarda işlenir ve arayüz
yanıt vermeyi sürdürür. Bölmenin ölçüsü sabit bir parça sayısı değil, geçen süredir: her
turda süre ölçülür ve bütçe dolduğunda tur bitirilir.

Kimi işler karede yapılmak zorunda değildir — kütüğe yazma, önbellek temizliği, sonraki
sayfanın hazırlanması. Bunlar için tarayıcı **boşta çalışma** sınıfından bir geri çağırım
sunar: kare işini bitirdiğinde kalan süre varsa çağrılır ve kalan süreyi bildirir. Boşta
çalışan geri çağırıma verilen iş, hiç çalışmama olasılığına karşı bir zaman aşımıyla
sınırlandırılır.

Ağır hesabın bir başka çözümü, işi ana iş parçacığından tümüyle çıkarmaktır; bu Web
İşçileri dersinin konusudur.

## Özet

- Tarayıcı ekranı kare kare yeniler; kare geri çağırımları biçem ve yerleşimden önce
  çalıştığı için orada yapılan değişiklik aynı karede görünür.
- Sabit gecikmeli zamanlayıcı, gecikmeyi işin bitişinden saydığı için kayma biriktirir ve
  kimi kareler güncelleme almaz; kare geri çağırımı hizayı korur.
- Kare geri çağırımı tek seferliktir, zaman damgası alır ve ekranı yenilenmeyen sekmede
  çağrılmaz; hareket geçen süreye göre hesaplanır.
- Ölçüm ile yazmanın sırayla yapılması her turda yerleşim hesabı zorlar; önce bütün
  ölçümler sonra bütün yazmalar yapıldığında bu zorlama ortadan kalkar.
- Kare bütçesini aşan iş parçalara bölünür ve parçalar arasında denetim çalışma zamanına
  geri verilir; ertelenebilir işler boşta çalışma geri çağırımına bırakılır.

## Sonraki Adım

Kare içinde yapılan işin bir bölümü metin üretmektir: ölçüm değerinin yanına birimi
yazmak, zaman damgasını okunur bir tarihe çevirmek, listeyi ada göre sıralamak. Bu
işlemlerin hiçbiri evrensel değildir. Ondalık ayırıcı her yerde aynı karakter değildir,
tarih alanlarının sırası dile göre değişir, harflerin sıralaması abeceye bağlıdır ve
Türkçede noktalı ile noktasız harf ayrı harflerdir. Elle yazılmış biçimleme kodu bu
farkları taşıyamaz. Sonraki ders, tarayıcının bu işleri yerel ayara göre yapan arayüzünü
ve o arayüzün kurulum maliyetini ele alır.
