Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
Dizi · 59/64 Dart Türkçe Tutorial
- Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
- Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
- Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
- Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
- Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
- Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
- Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
- Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
- Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
- Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
- Bölüm 3 — Konu 11: `if / else if / else`
- Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
- Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
- Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
- Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
- Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
- Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
- Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
- Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
- Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
- Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
- Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
- Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
- Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
- Bölüm 5 — Konu 26: Recursion (Özyineleme)
- Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
- Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
- Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
- Bölüm 6 — Konu 30: Getter / Setter
- Bölüm 6 — Konu 31: Statik Üyeler (`static`)
- Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
- Bölüm 7 — Konu 33: Metod Override Etme, `@override`
- Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
- Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
- Bölüm 7 — Konu 36: Mixin (`with`)
- Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
- Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
- Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
- Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
- Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
- Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
- Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
- Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
- Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
- Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
- Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
- Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
- Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
- Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
- Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
- Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
- Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
- Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
- Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
- Bölüm 12 — Konu 56: Extension Methods
- Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
- Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
- Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
- Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
- Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
- Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
- Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
- Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
- Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
İçindekiler 9 başlık
- Tek İş Parçacığı, Ama Nasıl "Eşzamanlı" Görünüyor?
- İki Kuyruk: Microtask Queue ve Event Queue
- await Aslında Ne Yapıyor?
- Bir Future Zincirinde Hata Yayılımı — Tam Mekanik
- Yakalanmayan Bir Hata — Ne Olur?
- Future.then() Zincirinde Hata Yayılımı
- Neden Bu Mekaniği Bilmek Önemli? — Pratik Sonuçlar
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Bölüm 10'da Future/async/await'i kullanmayı öğrenmiştik. Bu derste, bunların arka planda tam olarak nasıl çalıştığını — Dart'ın "event loop" mekanizmasını — işleyeceğiz.
Tek İş Parçacığı, Ama Nasıl "Eşzamanlı" Görünüyor?
Bölüm 12'de Dart'ın tek isolate'te (tek iş parçacığında) çalıştığını öğrenmiştik. Peki, tek bir iş parçacığı varsa, Future'lar nasıl "aynı anda" birden fazla işi yönetiyormuş gibi görünüyor? Cevap: event loop (olay döngüsü) mekanizması.
Zihinsel model: Dart'ın çalışma zamanı, sürekli dönen bir döngüdür — bu döngü, sırayla şunları kontrol eder: "yapılacak bir iş var mı? Varsa yap. Yoksa bekle." Future'lar, bu döngüye "ileride yapılacak işler" olarak eklenir.
İki Kuyruk: Microtask Queue ve Event Queue
Dart'ın event loop'u, iki ayrı kuyruk (queue) kullanır:
- Microtask Queue: Küçük, dahili işler için (genelde
Futuresonuçlarının işlenmesi). - Event Queue: Daha "büyük" dış olaylar için (I/O tamamlanması, zamanlayıcılar —
Timer, kullanıcı girdileri).
Kritik kural: Microtask queue, event queue'dan HER ZAMAN önce boşaltılır.
void main() {
print('1. Senkron kod');
Future(() => print('4. Event queue (Future ile eklendi)'));
Future.microtask(() => print('3. Microtask queue'));
print('2. Senkron kod (devam)');
}Çıktı:
1. Senkron kod
2. Senkron kod (devam)
3. Microtask queue
4. Event queue (Future ile eklendi)Bunu adım adım açıklayalım:
- Program, önce tüm senkron kodu çalıştırır (
1.ve2.satırları) — event loop, senkron kod çalışırken hiçbir şeye dokunmaz. - Senkron kod bittiğinde, event loop önce microtask queue'yu tamamen boşaltır (
3.). - Microtask queue tamamen boşaldıktan sonra, event queue'dan bir sonraki işi alır (
4.).
await Aslında Ne Yapıyor?
Bölüm 10'da await'in "Future'ı bekler" dediğimizi hatırlarsan, şimdi bunun tam mekaniğini görelim:
void main() async {
print('1');
await Future(() => print('bu bir future'));
print('3'); // bu satır, future TAMAMLANDIKTAN sonra çalışır
}await ile karşılaşıldığında, Dart şunu yapar: fonksiyonun geri kalanını (o satırdan sonraki her şeyi), bir microtask olarak "kaydeder" ve o Future tamamlanana kadar bu fonksiyonun çalışmasını duraklatır — ama bu, programın tamamının durması anlamına gelmez! Diğer senkron kod (varsa) ve diğer bekleyen işler, bu sırada çalışmaya devam edebilir.
Bir Future Zincirinde Hata Yayılımı — Tam Mekanik
Bölüm 8'de try/catch'i, Bölüm 10'da bunun asenkron kodla birlikte kullanımını öğrenmiştik. Şimdi bir hatanın, bir Future zincirinde tam olarak nasıl "yayıldığını" görelim:
Future<int> adim1() async {
print("Adım 1 çalışıyor");
throw Exception("Adım 1'de hata!");
}
Future<int> adim2() async {
print("Adım 2 çalışıyor"); // Bu satır HİÇ çalışmaz
return 42;
}
void main() async {
try {
int sonuc1 = await adim1(); // hata burada fırlatılır
int sonuc2 = await adim2(); // BU SATIRA HİÇ ULAŞILMAZ
print("Sonuç: $sonuc2");
} catch (e) {
print("Hata yakalandı: $e");
}
}Çıktı:
Adım 1 çalışıyor
Hata yakalandı: Exception: Adım 1'de hata!Mekanik olarak ne oluyor? adim1() içindeki throw, o Future'ı "hata durumunda tamamlanmış" (completed with error) olarak işaretler. await adim1() satırı, bu hatayı yakalar ve o anda fırlatır — tıpkı senkron bir throw gibi davranır. Bu, try/catch bloğunun onu doğal olarak yakalamasını sağlar. adim2()'ye hiç ulaşılmaz çünkü Dart, await satırında hata görür görmez, o try bloğunun geri kalanını atlayıp doğrudan catch'e geçer — tıpkı Bölüm 8'de öğrendiğimiz senkron throw davranışı gibi.
Yakalanmayan Bir Hata — Ne Olur?
Future<int> riskliIslem() async {
throw Exception("Yakalanmayan hata");
}
void main() async {
int sonuc = await riskliIslem(); // try/catch YOK!
print(sonuc); // bu satıra hiç ulaşılmaz
}Bu durumda, hata yakalanmadan üst katmanlara "yayılmaya" devam eder — eğer main()'in kendisi de bunu yakalamıyorsa, program bir "Unhandled exception" hatasıyla çöker. Bu, Bölüm 8'de öğrendiğimiz "hata, en yakın uygun try/catch'e kadar yukarı doğru yayılır" prensibinin, asenkron Future zincirlerinde de birebir aynı şekilde işlediğini gösteriyor.
Future.then() Zincirinde Hata Yayılımı
Bölüm 10 Konu 46'da öğrendiğimiz .then() zincirlerinde de aynı mantık geçerlidir:
void main() {
Future(() => throw Exception("Hata!"))
.then((veri) => print("Bu ÇALIŞMAZ: $veri"))
.then((veri) => print("Bu da ÇALIŞMAZ"))
.catchError((hata) => print("Yakalandı: $hata"));
}Bir .then() zincirinde hata oluştuğunda, zincirdeki sonraki tüm .then() blokları atlanır ve kontrol doğrudan en yakın .catchError()'a (veya zincirde bir .then()'in onError parametresine) geçer — tıpkı try/catch'te olduğu gibi.
Neden Bu Mekaniği Bilmek Önemli? — Pratik Sonuçlar
1. Debugging (hata ayıklama) kolaylaşır: Bir asenkron kod parçası beklenmedik sırada çalışıyorsa (örneğin bir print'in "olması gerekenden" önce/sonra çıktığını görüyorsan), microtask/event queue mantığını bilmek, neden böyle olduğunu anlamana yardımcı olur.
2. Performans sorunlarını anlama: Çok sayıda microtask, event queue'nun (kullanıcı etkileşimleri, zamanlayıcılar gibi) gecikmesine yol açabilir — çünkü microtask queue tamamen boşalana kadar event queue'ya sıra gelmez. Bu, Flutter'da UI'ın "tepkisiz" görünmesine yol açabilecek senaryoları anlamak için önemlidir.
3. Hata yönetimi tasarımı: Bir Future zincirinde nereye try/catch veya .catchError() koyman gerektiğine karar verirken, hatanın tam olarak nasıl yayılacağını bilmek, doğru yerde yakalamanı sağlar.
🎯 Bu Dersten Çıkarılması Gerekenler
- Dart'ın event loop'u, senkron kodu önce çalıştırır, sonra microtask queue'yu tamamen boşaltır, sonra event queue'dan bir iş alır — bu sıra kesindir.
await, fonksiyonun geri kalanını bir microtask olarak zamanlar ve o Future tamamlanana kadar (programın tamamını değil, sadece o fonksiyonu) duraklatır.- Bir
Futurezincirinde (async/await veya .then ile) oluşan hata, senkronthrowgibi davranır — en yakıntry/catchveya.catchError()'a kadar sonraki adımları atlayarak yayılır. - Yakalanmayan bir asenkron hata, programın "Unhandled exception" ile çökmesine yol açar.
- Bu mekaniği bilmek, asenkron kodun çalışma sırasını tahmin etmeni, performans sorunlarını anlamanı ve hata yönetimini doğru tasarlamanı sağlar.
📝 Ödevler
- [ ]
FutureveFuture.microtaskile senkronprintçağrılarını karışık sırada yazıp, çalıştırmadan önce çıktı sırasını tahmin et; sonra çalıştırıp doğrula. - [ ]
awaitkullanan iki adımlı bir asenkron fonksiyonda, ilk adımdathrowile hata fırlat, ikinci adımın hiç çalışmadığınıprintile kanıtla. - [ ]
.then()zincirinde bir hata oluştur, zincirdeki sonraki.then()bloklarının atlandığını,.catchError()'ın çalıştığını göster. - [ ] Yakalanmayan bir asenkron hata örneği yaz (try/catch olmadan), "Unhandled exception" davranışını gözlemle.
- [ ] Kendi cümlelerinle, "microtask queue neden her zaman event queue'dan önce boşaltılır" sorusunu, bu dersten öğrendiklerinle açıkla.
Sıradaki konu: Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, const Nesnelerin Bellek Avantajı