---
title: 'Olay Döngüsü'
source: 'https://academia.sh/tr/kurslar/eszamansiz-javascript/olay-dongusu'
course: 'Eşzamansız JavaScript ve Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:43+00:00'
license: 'CC BY-SA 4.0'
---

# Olay Döngüsü

Çağrı yığıtı, görev kuyruğu ve mikro görev kuyruğunun ayrımı; olay döngüsünün tur kuralı; karışık bir örnekte çıktı sırasının satır satır gerekçelendirilmesi.

Önceki ders geri çağrıların "sıraya girdiğini" söyledi. Sıra tek değildir ve içindeki
işler eşit öncelikli değildir. Bir söz geri çağrısı, kendisinden önce kaydedilmiş bir
zamanlayıcı geri çağrısından önce çalışır; bu, öncelik değil, kuyruk farkıdır.

Bu ders, o kuyrukları ve aralarındaki kuralı kurar. Kural öğrenildiğinde, karışık bir
programın çıktı sırası çalıştırmadan önce söylenebilir hale gelir — bu kursun geri kalanı
boyunca kullanılacak temel yetkinlik budur.

## Çağrı Yığıtı

**Çağrı yığıtı (call stack)**, o anda yürüyen fonksiyon çağrılarını tutan yapıdır.
Programlama Temelleri kursunda tanıtılan çağrı çerçevesi kavramı burada aynen geçerlidir:
her çağrı bir çerçeve iter, her `return` bir çerçeve çıkarır.

Yığıt boşaldığında, o anda yürüyen bir iş kalmamış demektir. Önceki dersteki sonuna kadar
çalışma kuralının kesin ifadesi budur: **bir sonraki iş, ancak yığıt boşaldığında
başlar.**

Bunun gözlenebilir bir sonucu vardır. Bir `try` bloğu, yalnız kendi yığıt çerçevesi
yaşarken oluşan hataları yakalar. Geri çağrı ise boş yığıtta, bambaşka bir turda çalışır:

```js
process.on("uncaughtException", (hata) => {
  console.log("2 — çalışma zamanı düzeyinde yakalandı:", hata.message);
});

try {
  setTimeout(() => {
    throw new Error("geri çağrının içinden");
  }, 0);
  console.log("1 — try bloğu sorunsuz bitti");
} catch (hata) {
  console.log("bu satır hiç çalışmaz:", hata.message);
}
```

```
1 — try bloğu sorunsuz bitti
2 — çalışma zamanı düzeyinde yakalandı: geri çağrının içinden
```

`try` bloğu yalnız `setTimeout` çağrısının kendisini kapsar; geri çağrının gövdesini
kapsamaz. Geri çağrı çalıştığında `try` bloğunun çerçevesi çoktan yığıttan çıkmıştır. Bu
gözlem, kursun ilerleyen bölümlerinde eşzamansız hata yönetiminin neden ayrı bir düzenek
gerektirdiğini açıklayacak.

## Görev Kuyruğu ve Olay Döngüsü

Ana ortam, tamamlanan işleri **görev kuyruğuna (task queue)** koyar: süresi dolan bir
zamanlayıcı, gelen bir ağ yanıtı, tıklanan bir düğme. Kuyruktaki her giriş bir
**görev (task)** — bazı kaynaklarda makro görev — yani baştan sona çalıştırılacak bir
geri çağrıdır.

**Olay döngüsü (event loop)**, iki adımı sonsuza kadar tekrarlayan denetim akışıdır:

1. Yığıt boşsa, görev kuyruğundan bir görev al ve sonuna kadar çalıştır.
2. Görev bittikten sonra, mikro görev kuyruğunu tamamen boşalt.

Bu iki adıma **tur** denir. Tarayıcı ortamında turların arasına çizim (render) adımı da
girer; bu nedenle uzun bir görev, çizimi de erteler.

## Mikro Görev Kuyruğu

**Mikro görev (microtask)**, aynı turun sonunda çalışması güvence altına alınmış küçük
iştir. Söz geri çağrıları ve `queueMicrotask` ile kaydedilen işler bu kuyruğa girer.

İki kuyruk arasındaki fark tek cümleyle ifade edilir: **görev kuyruğundan tur başına bir
iş alınır; mikro görev kuyruğu ise tamamen boşaltılır.**

Boşaltma sırasında eklenen yeni mikro görevler de aynı boşaltmaya dahildir. Yani bir mikro
görev, başka bir mikro görev üretirse, o da sıradaki göreve geçilmeden önce çalışır.

```js
setTimeout(() => {
  console.log("A — birinci görev");
  queueMicrotask(() => console.log("B — birinci görevin mikro görevi"));
  setTimeout(() => console.log("D — birinci görevin ürettiği görev"), 0);
}, 0);

setTimeout(() => console.log("C — ikinci görev"), 0);
```

```
A — birinci görev
B — birinci görevin mikro görevi
C — ikinci görev
D — birinci görevin ürettiği görev
```

Birinci görev, hem bir mikro görev hem bir görev üretti. Mikro görev, ikinci göreve
geçilmeden önce çalıştı; üretilen görev ise kuyruğun sonuna eklendiği için en sona kaldı.

## Karışık Bir Örnekte Sıra

Kural, eşzamanlı kod ile iki kuyruğun birlikte göründüğü bir örnekte sınanır. Aşağıdaki
programın çıktı sırasını, çalıştırmadan önce söyleyebilmelisiniz.

```js
console.log("1 — eşzamanlı");
setTimeout(() => console.log("6 — görev kuyruğu (setTimeout)"), 0);
Promise.resolve().then(() => console.log("4 — mikro görev (söz)"));
queueMicrotask(() => console.log("5 — mikro görev (queueMicrotask)"));
console.log("2 — eşzamanlı");
(function () {
  console.log("3 — eşzamanlı (iç fonksiyon)");
})();
```

```
1 — eşzamanlı
2 — eşzamanlı
3 — eşzamanlı (iç fonksiyon)
4 — mikro görev (söz)
5 — mikro görev (queueMicrotask)
6 — görev kuyruğu (setTimeout)
```

Gerekçe satır satır şudur:

- **1, 2, 3** — Bunlar yürüyen işin kendisidir. Yığıt boşalana kadar hiçbir kuyruğa
  bakılmaz. Ara satırlardaki `setTimeout`, `then` ve `queueMicrotask` çağrıları yalnız
  *kayıt* yapar; kaydettikleri fonksiyonu çalıştırmaz.
- **4, 5** — Yığıt boşaldı. Mikro görev kuyruğu tamamen boşaltılır. İçindeki iki iş,
  kaydedilme sırasına göre çalışır: önce söz geri çağrısı, sonra `queueMicrotask`.
- **6** — Mikro görev kuyruğu boş. Ancak şimdi görev kuyruğundan bir görev alınır.

Sıranın kaynağı önceliğin kendisi değil, kuyruk yapısıdır: `setTimeout` en başta
kaydedildiği halde en sona kaldı; çünkü farklı bir kuyruğa girdi.

## Mikro Görev Kuyruğu Aç Bırakabilir

"Tamamen boşaltılır" kuralının bir bedeli vardır: kendini yeniden kaydeden bir mikro görev
zinciri, görev kuyruğunu süresiz bekletir.

```js
let sayac = 0;

setTimeout(() => console.log("zamanlayıcı çalıştı; biriken mikro görev:", sayac), 0);

function zincir() {
  sayac += 1;
  if (sayac < 100000) queueMicrotask(zincir);
}
queueMicrotask(zincir);
```

```
zamanlayıcı çalıştı; biriken mikro görev: 100000
```

Yüz bin mikro görevin tamamı, sıfır gecikmeli zamanlayıcıdan önce çalıştı. Sayaç sınırı
kaldırılsaydı zamanlayıcı hiç çalışmayacaktı. Buna **açlık (starvation)** denir: bir
kuyruk, diğerinin çalışma fırsatını tüketir.

Pratik sonucu şudur: uzun ve bölünebilir bir işi mikro görevlere bölmek çözüm değildir.
Ana ortama gerçekten sıra vermek için işi görev kuyruğuna aktarmak gerekir. Bu kalıp,
başarım konusunda ayrıntılandırılacak.

## Kuralı Ölçüm Akışına Uygulamak

Kural, kursun örneğinde de aynen işler. Aşağıdaki program iki istasyondan ölçüm ister;
istasyonların yanıt gecikmeleri farklıdır ve yanıt geldiğinde bir kayıt işi mikro göreve
bırakılır.

```js
const KAYNAK = {
  A1: { gecikme: 30, deger: 21.4 },
  B2: { gecikme: 10, deger: 19.8 },
};

function olcumIste(istasyon, gericagri) {
  console.log("istek kaydedildi:", istasyon);
  const kayit = KAYNAK[istasyon];
  setTimeout(() => gericagri({ istasyon, deger: kayit.deger }), kayit.gecikme);
}

olcumIste("A1", (olcum) => {
  console.log("görev: yanıt işleniyor —", olcum.istasyon);
  queueMicrotask(() => console.log("mikro görev: kayıt tamamlandı —", olcum.istasyon));
});

olcumIste("B2", (olcum) => {
  console.log("görev: yanıt işleniyor —", olcum.istasyon);
  queueMicrotask(() => console.log("mikro görev: kayıt tamamlandı —", olcum.istasyon));
});

console.log("betiğin eşzamanlı kısmı bitti");
```

```
istek kaydedildi: A1
istek kaydedildi: B2
betiğin eşzamanlı kısmı bitti
görev: yanıt işleniyor — B2
mikro görev: kayıt tamamlandı — B2
görev: yanıt işleniyor — A1
mikro görev: kayıt tamamlandı — A1
```

İki gözlem önemlidir. Birincisi, istek sırası ile yanıt sırası aynı değildir: A1 önce
istendi, B2 önce yanıtladı. Eşzamansız bir akışta sonuçların geliş sırası, isteklerin
başlatılma sırasına bağlı değildir. İkincisi, her yanıtın kayıt mikro görevi kendi turunun
sonunda, bir sonraki yanıt işlenmeden çalışır; iki istasyonun işleme adımları birbirine
karışmaz.

## Kuyruk Ayrıntıları Ortama Bağlıdır

İki kuyruklu model standarttır ve her ortamda geçerlidir. Buna karşılık çalışma zamanları
görev kuyruğunu evrelere bölebilir ve kendi ek kuyruklarını ekleyebilir. Bu ek kuyrukların
sözlerin mikro görev kuyruğuna göre yeri, aynı çalışma zamanında bile bağlama göre
değişebilir.

Aşağıdaki iki dosya aynı üç satırı içerir; yalnız modül biçimleri farklıdır. Modül
biçimlerinin kendisi Modüller, Araçlar ve Ekosistem kursunun konusudur.

```js
// sira.cjs
Promise.resolve().then(() => console.log("söz mikro görevi"));
process.nextTick(() => console.log("çalışma zamanına özgü kuyruk"));
console.log("eşzamanlı");
```

```
$ node sira.cjs
eşzamanlı
çalışma zamanına özgü kuyruk
söz mikro görevi
```

```js
// sira.mjs
Promise.resolve().then(() => console.log("söz mikro görevi"));
process.nextTick(() => console.log("çalışma zamanına özgü kuyruk"));
console.log("eşzamanlı");
```

```
$ node sira.mjs
eşzamanlı
söz mikro görevi
çalışma zamanına özgü kuyruk
```

İki kuyruğun göreli sırası ters döndü. Buradan çıkarılacak kural, hangi biçimin doğru
olduğu değildir: **taşınabilir kod, standart dışı kuyrukların göreli sırasına
dayanmamalıdır.** Eşzamanlı kod, mikro görevler ve görevler arasındaki üç aşamalı sıra
her ortamda güvenilirdir; ötesi ortamın ayrıntısıdır.

## Özet

- Çağrı yığıtı yürüyen çağrıları tutar; bir sonraki iş ancak yığıt boşaldığında başlar.
- Olay döngüsü her turda görev kuyruğundan bir görev alır, ardından mikro görev kuyruğunu
  tamamen boşaltır.
- Mikro görevler aynı turda, görevler sonraki turlarda çalışır; bu nedenle sonradan
  kaydedilen bir söz geri çağrısı, önceden kaydedilen bir zamanlayıcıdan önce çalışır.
- Boşaltma sırasında üretilen mikro görevler de aynı boşaltmaya dahildir; bu, görev
  kuyruğunu aç bırakabilir.
- Üç aşamalı sıra (eşzamanlı kod, mikro görevler, görevler) standarttır; çalışma zamanına
  özgü ek kuyrukların yeri taşınabilir değildir.

## Sonraki Adım

Kuyruk kuralı kurulduğuna göre, kuyruğa iş koyan en yaygın araca dönülebilir:
zamanlayıcılar. Sonraki ders, `setTimeout` içindeki sayının neden bir söz değil bir alt
sınır olduğunu, eşit gecikmeli zamanlayıcıların hangi sıraya girdiğini ve yinelemeli
çalıştırmanın kendi geri çağrısı uzun sürdüğünde nasıl kaydığını gösterir. Ölçüm akışının
ilk somut parçası — zamanlayıcıya dayalı sahte bir istasyon — orada kurulacak.
