Stream əsasları
Stream<T> — zamanla gələn dəyərlər ardıcıllığıdır. Future bir dəfə tamamlanır, Stream isə sıfır, bir və ya sonsuz sayda data hadisəsi göndərir, sonra ya done ilə bitir, ya da error verir (error-dan sonra da davam edə bilər).
Müqayisə üçün ən sadə model: Future<T> ≈ bir dəyər, Stream<T> ≈ Iterable<T>-in asinxron variantı.
Stream-i istehlak etməyin iki yolu var:
stream.listen(onData, onError:, onDone:, cancelOnError:)— geriStreamSubscriptionalırsan və nəzarət səndədir (pause, resume, cancel).await for (final x in stream) { ... }—asyncfunksiya daxilində dövr kimi.breakvə yareturnavtomatik olaraq subscription-ı ləğv edir.
Stream passivdir: listen çağırılana qədər (single-subscription halda) heç bir iş başlamır.
| Xüsusiyyət | Single-subscription (default) | Broadcast |
|---|---|---|
| Dinləyici sayı | Ömrü boyu **bir dənə**; ikinci `listen` `StateError` atır | İstənilən qədər, istənilən vaxt |
| `listen`-dən əvvəlki hadisələr | Bufer olunur — dinləyici hamısını alır | **İtir** — gec qoşulan əvvəlkiləri görmür |
| Pause davranışı | Mənbə dayanır və ya buferləyir (backpressure işləyir) | Mənbə dayanmır — hadisələr həmin dinləyici üçün buferlənir |
| Tipik mənbə | Fayl oxuma, HTTP cavabı, `async*` generator | İstifadəçi hadisələri, event bus, `Timer`-lər |
| Necə yaradılır | `StreamController<T>()` | `StreamController<T>.broadcast()` və ya `stream.asBroadcastStream()` |
Ləğvetmə öhdəliyi. listen çağırdığın hər yerdə sən bir öhdəlik götürürsən: subscription-ı cancel() etmək. Ləğv edilməyən subscription:
- callback-i və onun tutduğu (capture etdiyi) bütün obyektləri canlı saxlayır — yaddaş sızması;
- obyekt "ölmüş" olsa belə hadisə emal etməyə davam edir — dispose olunmuş state-ə yazmaq cəhdləri;
- mənbəni açıq saxlayır (socket, timer, platform kanalı) — batareya və trafik xərci.
Qayda sadədir: `listen` harada çağırılırsa, `cancel` da orada planlanmalıdır. Uzun ömürlü obyektdə subscription-ı sahədə saxla və dispose()-da ləğv et. Flutter-də bu, StatefulWidget-in dispose() metodudur; await for istifadə edirsənsə ləğvetmə avtomatikdir.
StreamController üçün əlavə öhdəlik: close(). Bağlanmayan controller done göndərmir, ona görə await for dövrəsi heç vaxt bitmir və controller-i tutan obyekt sərbəst buraxılmır.
Stream real tətbiqlərdə harada görünür? Cavabı hazır saxla — müsahibədə "nümunə ver" sualı mütləq gəlir:
- İstifadəçi girişi: axtarış sahəsinə yazılan mətn (debounce ilə), scroll mövqeyi, jest hadisələri.
- Şəbəkə: WebSocket, Server-Sent Events, böyük HTTP cavabının parça-parça oxunması (
response.stream). - Platform: geolokasiya yeniləmələri, sensor və akselerometr, batareya statusu, şəbəkə bağlantısının dəyişməsi (
connectivity), bildirişlər. - Verilənlər bazası: Firestore
snapshots(), Drift/Isarwatch()— sorğu nəticəsi dəyişəndə yeni hadisə gəlir. - Daxili event bus: auth statusunun dəyişməsi, dil dəyişikliyi — bunlar tipik broadcast stream-lərdir.
Mühüm nüans: bu mənbələrin əksəriyyəti broadcast olmalıdır, çünki bir neçə yerdən dinlənilir. Repository-də Stream qaytarırsansa, single-subscription qaytarmaq istifadəçini gözlənilməz StateError-a apara bilər.
Müsahibə ipucu. Klassik sual: "single-subscription və broadcast stream fərqi nədir?" Güclü cavab yalnız "dinləyici sayı" ilə bitmir — üç fərqi de: (1) dinləyici sayı; (2) buferləmə — single-subscription listen-ə qədər hadisələri saxlayır, broadcast isə saxlamır, gec qoşulan itirir; (3) backpressure — single-subscription-da pause() mənbəyi dayandıra bilir, broadcast-da yox.
İkinci sual: "subscription-ı ləğv etməsən nə olar?" Cavab: yaddaş sızması + dispose olunmuş obyektə yazma cəhdi + mənbənin açıq qalması. Bir cümlə ilə yekunlaşdır: `listen` bir öhdəlikdir, `cancel` isə onun ödənişidir.
Ən çox edilən səhv: Stream-i "bir neçə dəyər qaytaran Future" kimi izah edib buferləmə və ləğvetmə fərqlərini buraxmaq.
📚 Mənbələr və sənədlər
- Stream-lərdən istifadərəsmidart.dev
- Stream classrəsmiapi.dart.dev