İçeriğe geç
academia.sh

Ders 03 / 17

Zamanlayıcılar

Gecikmenin alt sınır oluşu, eşit gecikmeli zamanlayıcıların sırası, yinelemeli çalıştırmada kayma ve üst üste binme, kendi kendini zamanlayan döngü ve iptal.

İçindekiler

Önceki ders, kuyruğa iş koyan çağrıların yalnız kayıt yaptığını gösterdi. En sık kullanılan kayıt aracı zamanlayıcılardır ve verilen sayı çoğu zaman yanlış okunur: setTimeout(f, 10) çağrısı, f’nin on milisaniye sonra çalışacağını söylemez.

Bu ders o sayının ne söylediğini, eşit gecikmeli zamanlayıcıların hangi sıraya girdiğini ve yinelemeli çalıştırmanın nerede kaydığını kurar. Ölçüm akışının ilk çalışan parçası da burada, zamanlayıcıya dayalı sahte bir istasyon olarak ortaya çıkacak.

Gecikme Bir Alt Sınırdır

setTimeout(geriCagri, gecikme) çağrısının anlamı şudur: geçen süre gecikme değerine ulaşmadan geri çağrı kuyruğa alınmaz. Kuyruğa alındıktan sonra ne zaman çalışacağı, önündeki işlerin ne kadar süreceğine bağlıdır.

Aynı anda kaydedilen zamanlayıcılar, gecikmelerine göre sıraya girer; gecikmeleri eşitse kaydedilme sıralarını korurlar.

setTimeout(() => console.log("4 — 20 ms"), 20);
setTimeout(() => console.log("3 — 10 ms"), 10);
setTimeout(() => console.log("1 — 0 ms, ilk kayıt"), 0);
setTimeout(() => console.log("2 — 0 ms, ikinci kayıt"), 0);
1 — 0 ms, ilk kayıt
2 — 0 ms, ikinci kayıt
3 — 10 ms
4 — 20 ms

Sıfır gecikme “hemen” anlamına gelmez, “en erken fırsatta” anlamına gelir. Ana ortamlar ayrıca bir taban gecikme uygular: HTML standardı, beşten derin iç içe geçmiş zamanlayıcılarda alt sınırı dört milisaniyeye çıkarır; sunucu çalışma zamanlarında da sıfır gecikme genellikle bir milisaniyeye yuvarlanır. Sayı ne olursa olsun, alt sınırdan öteye söz verilmez.

Engelleme Alt Sınırı Aşar

Alt sınırın üst sınırı yoktur. Önceki turdaki iş uzun sürerse, süresi çoktan dolmuş bir zamanlayıcı kuyrukta bekler.

function meshgulBekle(sure) {
  const bitis = Date.now() + sure;
  while (Date.now() < bitis) {
    // İş parçacığını kasten meşgul tutar.
  }
}

setTimeout(() => {
  console.log("1 — 10 ms zamanlayıcısı; 50 ms boyunca iş parçacığını tutuyor");
  meshgulBekle(50);
  console.log("2 — 10 ms zamanlayıcısı bitti");
}, 10);

setTimeout(() => console.log("3 — 20 ms zamanlayıcısı"), 20);
1 — 10 ms zamanlayıcısı; 50 ms boyunca iş parçacığını tutuyor
2 — 10 ms zamanlayıcısı bitti
3 — 20 ms zamanlayıcısı

İkinci zamanlayıcının süresi, birinci geri çağrı hâlâ çalışırken doldu. Sonuna kadar çalışma kuralı gereği araya giremedi; birinci iş bitene kadar bekledi. Bu, ölçülen gecikmenin neden istenen gecikmeden büyük olabildiğinin tek nedenidir: iş parçacığı meşgul.

Yinelemeli Çalıştırma ve Üst Üste Binme

setInterval(geriCagri, aralik), geri çağrıyı belirli aralıklarla kuyruğa koyar. Aralık, geri çağrının bitişinden sonraki değil, kaydedilme anlarındaki sürelerle ölçülür. Geri çağrı aralıktan uzun sürerse, ardışık çalıştırmalar arasında boşluk kalmaz.

Aşağıdaki iki program yalnız tek satırla ayrılır. Birincisinde geri çağrı kısa:

let tik = 0;
const kimlik = setInterval(() => {
  tik += 1;
  console.log("aralık tiki", tik);
  if (tik === 3) clearInterval(kimlik);
}, 5);

setTimeout(() => console.log("30 ms zamanlayıcısı"), 30);
aralık tiki 1
aralık tiki 2
aralık tiki 3
30 ms zamanlayıcısı

Üç tik beşer milisaniyelik aralıklarla tamamlandı ve otuzuncu milisaniyedeki zamanlayıcı sıraya girdi. İkincisinde ise her tik yirmi milisaniye iş yapıyor:

function meshgulBekle(sure) {
  const bitis = Date.now() + sure;
  while (Date.now() < bitis) {
    // İş parçacığını kasten meşgul tutar.
  }
}

let tik = 0;
const kimlik = setInterval(() => {
  tik += 1;
  console.log("aralık tiki", tik);
  meshgulBekle(20);
  if (tik === 3) clearInterval(kimlik);
}, 5);

setTimeout(() => console.log("30 ms zamanlayıcısı"), 30);
aralık tiki 1
aralık tiki 2
30 ms zamanlayıcısı
aralık tiki 3

Sıra değişti. İkinci tik, otuzuncu milisaniye gelmeden başladı ve yirmi milisaniye boyunca iş parçacığını tuttu; zamanlayıcı ancak o iş bittikten sonra çalışabildi. Üçüncü tik ise arkasından, neredeyse ara vermeden geldi.

Bu davranışın iki sonucu vardır. Birincisi, setInterval ile kurulan bir akışta geri çağrılar arasında dinlenme süresi güvence altında değildir. İkincisi, geri çağrı içinde eşzamansız iş başlatılıyorsa, önceki çalıştırma bitmeden yenisi başlayabilir ve iki iş üst üste biner.

Kendi Kendini Zamanlayan Döngü

Her çalıştırma arasında en az bir aralık boşluk isteniyorsa, yineleme setTimeout ile kurulur: geri çağrı işini bitirdikten sonra bir sonrakini kendisi kaydeder.

Bu kalıp, ölçüm akışının ilk çalışan biçimidir. Sahte istasyonlar, gecikmeleri ve değerleri sabit bir tablodan okunur; böylece program her çalıştırmada aynı çıktıyı verir.

const KAYNAK = {
  A1: { gecikme: 30, deger: 21.4 },
  B2: { gecikme: 10, deger: 19.8 },
  C3: { gecikme: 20, deger: 23.1 },
};

function akisBasla(istasyonlar, aralik, gericagri) {
  let sayac = 0;
  let kimlik = null;
  let durduruldu = false;

  function tur() {
    const istasyon = istasyonlar[sayac % istasyonlar.length];
    sayac += 1;
    gericagri({ sira: sayac, istasyon, deger: KAYNAK[istasyon].deger });
    if (!durduruldu) kimlik = setTimeout(tur, aralik);
  }

  kimlik = setTimeout(tur, aralik);

  return function durdur() {
    durduruldu = true;
    clearTimeout(kimlik);
  };
}

const durdur = akisBasla(["A1", "B2", "C3"], 5, (olcum) => {
  console.log(olcum.sira, olcum.istasyon, olcum.deger);
  if (olcum.sira === 4) durdur();
});
1 A1 21.4
2 B2 19.8
3 C3 23.1
4 A1 21.4

durduruldu bayrağı gereksiz görünebilir; değildir. durdur işlevi, geri çağrının içinden çağrılır ve o sırada bir sonraki tur henüz kaydedilmemiştir. Yalnız clearTimeout çağrılsaydı, çağrı zaten çalışmış bir zamanlayıcıyı iptal eder, hemen ardından tur yeni bir zamanlayıcı kaydederdi ve akış hiç durmazdı. Kendi kendini zamanlayan döngülerde iptal, bir durum bayrağıyla birlikte kurulmalıdır.

Bekleyen Zamanlayıcı Programı Ayakta Tutar

Bekleyen bir zamanlayıcı, ana ortam için “yapılacak iş var” demektir. Sunucu çalışma zamanı, bekleyen iş kalmayana kadar kapanmaz.

process.on("exit", () => console.log("3 — çalışma zamanı kapanıyor"));

setTimeout(() => console.log("2 — bekleyen zamanlayıcı çalıştı"), 50);
console.log("1 — betiğin eşzamanlı kısmı bitti");
1 — betiğin eşzamanlı kısmı bitti
2 — bekleyen zamanlayıcı çalıştı
3 — çalışma zamanı kapanıyor

Aynı program clearTimeout ile iptal edilirse, kapanış hemen gerçekleşir:

process.on("exit", () => console.log("2 — çalışma zamanı kapanıyor"));

const kimlik = setTimeout(() => console.log("bu satır hiç çalışmaz"), 50);
clearTimeout(kimlik);
console.log("1 — betiğin eşzamanlı kısmı bitti");
1 — betiğin eşzamanlı kısmı bitti
2 — çalışma zamanı kapanıyor

Bu gözlem ileride iki yerde işe yarayacak: iptal edilmemiş bir zamanlayıcı hem programın kapanmasını geciktirir hem de geri çağrısının kapattığı bütün değerleri bellekte tutar. İkincisi bellek konusunda ele alınacak sızıntı türlerinden biridir.

Zamanlayıcı Ne Değildir

Üç yaygın yanlış okuma vardır ve üçü de aynı kaynaktan çıkar.

setTimeout(f, 0) “hemen çalıştır” değildir; “yığıt boşalınca çalıştır” demektir. Bu nedenle uzun bir işi ikiye bölmek için kullanılabilir, ama işi hızlandırmaz.

Zamanlayıcı bir kronometre değildir. Geri çağrı içinde geçen süreyi ölçmek için zamanlayıcı gecikmesine güvenilemez; ölçüm ayrı bir araçla yapılır.

Zamanlayıcı bir eşzamanlılık aracı da değildir. İki geri çağrı asla aynı anda çalışmaz; biri bitmeden diğeri başlamaz. Beklemeler üst üste biner, hesaplar binmez.

Özet

  • Zamanlayıcı gecikmesi bir alt sınırdır; üst sınır yoktur ve meşgul iş parçacığı bu sınırı serbestçe aşar.
  • Eşit gecikmeli zamanlayıcılar kaydedilme sıralarını korur.
  • setInterval çalıştırmalar arasında boşluk güvencesi vermez; uzun süren geri çağrılarda tikler üst üste biner.
  • Kendi kendini zamanlayan setTimeout döngüsü her tur arasında en az bir aralık bırakır; iptali için durum bayrağı gerekir.
  • Bekleyen bir zamanlayıcı çalışma zamanını ayakta tutar ve geri çağrısının kapattığı değerleri bellekte tutar.

Sonraki Adım

Zamanlayıcılar geri çağrıyı zamana bağladı; sıradaki adım geri çağrıyı sonuca bağlamak. Sonraki ders, ölçüm kaynağını hata bildirebilen bir geri çağrı arayüzüne dönüştürür; üç istasyonu sırayla ve birlikte okumayı dener; ardından bu yaklaşımın sınırlarını gösterir: iç içe geçme, hata yollarının tekrarı ve geri çağrıyı çağıran tarafa bırakmanın getirdiği belirsizlikler. Sözler bu sınırların yanıtı olarak doğacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat