Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
Dizi · 46/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 10 başlık
- Future ile Stream Arasındaki Temel Fark
- async* — Stream Üreten Fonksiyon
- yield — Bir Değer "Yayınlamak"
- .listen() — Stream'i "Dinlemek"
- Stream'i await for ile Dinleme — async/await Tarzı
- Gerçek Dünya Senaryoları — Stream Ne Zaman Kullanılır?
- Stream Hata Yönetimi — try/catch ile
- Sonlu vs Sonsuz Stream
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Future ile Stream Arasındaki Temel Fark
Bir önceki derste Future'ın "gelecekte bir kere gelecek bir değeri" temsil ettiğini öğrenmiştik. Peki ya bir kaynaktan zamanla, birden fazla değer gelecekse? İşte bunun için Stream kullanılır.
Basit bir benzetme:
Future= bir paket kargo (bir kere gelir, elinde tutarsın)Stream= bir haber bülteni aboneliği (zamanla, birden fazla haber gelir)
Stream<int> sayiUret() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i; // her saniyede bir değer "yayınla"
}
}
void main() {
sayiUret().listen((sayi) {
print("Alınan sayı: $sayi");
});
}Çıktı (her biri 1 saniye arayla):
Alınan sayı: 1
Alınan sayı: 2
Alınan sayı: 3
Alınan sayı: 4
Alınan sayı: 5async* — Stream Üreten Fonksiyon
Bölüm 10'da async fonksiyonların Future döndürdüğünü öğrenmiştik. async* (yıldızlı versiyon), bunun yerine bir Stream döndüren bir fonksiyon tanımlar:
Stream<int> sayac() async* {
// fonksiyon gövdesi, zamanla birden fazla değer "yayınlayabilir"
}Dönüş tipi her zaman Stream<T>'dir — tıpkı async'in her zaman Future<T> döndürmesi gibi.
yield — Bir Değer "Yayınlamak"
yield, bir Stream'e yeni bir değer eklemenin yoludur — return gibi fonksiyonu sonlandırmaz, sadece bir değeri "dışarı gönderir" ve fonksiyon çalışmaya devam eder:
Stream<String> mesajUret() async* {
yield "Birinci mesaj";
await Future.delayed(Duration(seconds: 1));
yield "İkinci mesaj";
await Future.delayed(Duration(seconds: 1));
yield "Üçüncü mesaj";
}
void main() {
mesajUret().listen((mesaj) {
print(mesaj);
});
}Bu, Bölüm 5'te öğrendiğimiz return'den temelde farklıdır: return, fonksiyonu bir kere sonlandırıp tek bir değer döndürür; yield, fonksiyonu sonlandırmadan art arda birden fazla değer gönderebilir.
.listen() — Stream'i "Dinlemek"
void main() {
Stream<int> stream = sayiUret();
stream.listen(
(veri) {
print("Veri geldi: $veri");
},
onError: (hata) {
print("Hata: $hata");
},
onDone: () {
print("Stream tamamlandı!");
},
);
}.listen(), bir Stream'e abone olmanın yoludur. Üç önemli callback (geri çağırma fonksiyonu) alabilir:
- İlk parametre (zorunlu): her yeni veri geldiğinde çalışır.
onError: stream içinde bir hata oluşursa çalışır.onDone: stream tamamen bittiğinde (artık yeni veri gelmeyeceğinde) çalışır.
Stream'i await for ile Dinleme — async/await Tarzı
.listen() yerine, async/await'e benzer, daha okunabilir bir sözdizimi de kullanılabilir:
Stream<int> sayiUret() async* {
for (int i = 1; i <= 3; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (var sayi in sayiUret()) {
print("Sayı: $sayi");
}
print("Stream bitti");
}await for, Bölüm 3'te öğrendiğimiz for-in döngüsünün asenkron versiyonudur — her yeni değer geldiğinde döngü gövdesi çalışır, stream bitene kadar bu devam eder. Bu, .listen()'e göre genelde daha okunabilir bulunur, özellikle stream'den gelen her değeri sırayla işlemen gerektiğinde.
Gerçek Dünya Senaryoları — Stream Ne Zaman Kullanılır?
Future, "bir kerelik" asenkron işlemler için uygundur (bir API isteği, bir dosya okuma). Stream ise zamanla tekrar tekrar gelen veriler için kullanılır:
- Kullanıcının yazdığı her karakteri anlık dinlemek (arama kutusu)
- Bir sensörden (konum, ivmeölçer) sürekli veri almak
- Bir sohbet uygulamasında gelen her yeni mesajı dinlemek
- Bir dosyanın, parça parça (chunk chunk) okunması
// Kavramsal örnek — gerçek bir sohbet uygulamasının basitleştirilmiş hali
Stream<String> mesajlariDinle() async* {
// Gerçek bir uygulamada bu, bir sunucudan gelen WebSocket verisi olurdu
await Future.delayed(Duration(seconds: 1));
yield "Ahmet: Merhaba!";
await Future.delayed(Duration(seconds: 2));
yield "Ayşe: Selam, nasılsın?";
}
void main() async {
print("Sohbete katılındı, mesajlar dinleniyor...");
await for (var mesaj in mesajlariDinle()) {
print(mesaj);
}
}Stream Hata Yönetimi — try/catch ile
Stream<int> riskliStream() async* {
yield 1;
yield 2;
throw Exception("Stream içinde hata oluştu");
}
void main() async {
try {
await for (var deger in riskliStream()) {
print(deger);
}
} catch (e) {
print("Hata yakalandı: $e");
}
}Çıktı:
1
2
Hata yakalandı: Exception: Stream içinde hata oluştuBölüm 8'de öğrendiğimiz try/catch, await for ile de tutarlı bir şekilde çalışır — stream içinde bir hata fırlatılırsa, await for döngüsü durur ve hata catch bloğuna düşer.
Sonlu vs Sonsuz Stream
// Sonlu stream — belirli sayıda değer üretip biter
Stream<int> sonluStream() async* {
yield 1;
yield 2;
yield 3;
} // fonksiyon bittiğinde stream de "tamamlanır" (onDone tetiklenir)
// Sonsuz stream — teorik olarak hiç bitmez (dikkatli kullanılmalı!)
Stream<int> sonsuzStream() async* {
int sayac = 0;
while (true) {
await Future.delayed(Duration(seconds: 1));
yield sayac++;
}
}Bölüm 3'te while (true) için verdiğimiz uyarı burada da geçerli — sonsuz bir stream oluşturursan, onu dinleyen tarafın bir noktada dinlemeyi durdurması (.cancel() ile, bir sonraki konuda değinebiliriz) veya uygulamanın yaşam döngüsüyle yönetilmesi gerekir, aksi halde kaynak sızıntısına (resource leak) yol açabilir.
🎯 Bu Dersten Çıkarılması Gerekenler
Stream, zamanla birden fazla değer üreten bir asenkron kaynağı temsil eder (Future'ın aksine, tek bir değer değil).async*, birStream<T>döndüren fonksiyon tanımlar;yield, fonksiyonu sonlandırmadan bir değer "yayınlar"..listen(), bir stream'e abone olmanın temel yoludur;onErrorveonDonecallback'leri alabilir.await for, stream'ifor-in'e benzer, okunabilir bir şekilde dinlemenin yoludur.try/catch, stream içindeki hataları da senkron kod gibi yakalayabilir.
📝 Ödevler
- [ ]
async*veyieldkullanarak, 1'den 5'e kadar sayıları birer saniye arayla üreten bir Stream yaz,.listen()ile dinle. - [ ] Aynı Stream'i
await forile dinle, iki yaklaşımı karşılaştır. - [ ]
.listen()'inonDonecallback'ini kullanarak, stream tamamlandığında özel bir mesaj yazdır. - [ ] Bir stream içinde
throwile hata fırlat,try/catch(await for ile) kullanarak yakala. - [ ] Kendi senaryonu düşün (örn. bir sipariş takip sistemi, sırayla durum güncellemeleri yayınlayan) ve bunu bir Stream ile modelle.
Sıradaki konu: Bölüm 10 — Konu 48: StreamController, Broadcast Stream