Sparround

Event loop və microtask növbəsi

Dart isolate-i tək thread-lidir: bir anda yalnız bir kod parçası işləyir. Paralellik illüziyasını event loop yaradır — o, sonsuz dövrədə növbələrdən iş götürüb icra edir.

İki növbə var və onların prioriteti fərqlidir:

  • Microtask queue — daxili, qısa işlər üçün: scheduleMicrotask, Future.microtask, tamamlanmış future-a bağlanmış then callback-ləri.
  • Event queue — xarici hadisələr üçün: Timer (o cümlədən Future.delayed), Future(() {}), I/O, şəbəkə cavabı, istifadəçi hadisələri, isolate mesajları.

Dövrənin qaydası bir cümlədədir: event queue-dan bir iş götürməzdən əvvəl microtask queue tamamilə boşaldılır. Yəni:

1. Cari sinxron kod sona qədər işləyir (heç nə onu kəsə bilmir). 2. Microtask queue boşalana qədər işlənir — bu zaman əlavə olunan yeni microtask-lar da elə indi işlənir. 3. Event queue-dan bir iş götürülür və icra olunur. 4. Yenidən 2-ci addıma qayıdılır.

YazılışHansı növbəyə düşürNə vaxt istifadə et
`scheduleMicrotask(fn)`MicrotaskCari sinxron blokdan dərhal sonra işləməli qısa daxili iş
`Future.microtask(fn)`MicrotaskEyni şey, amma nəticəni `Future` kimi almaq lazımdırsa
`completedFuture.then(fn)`MicrotaskAdi zəncirləmə — özün seçmirsən, dil belə edir
`Future(fn)` / `Future.delayed(Duration.zero, fn)`Event queue (timer)İşi "sonraya" atmaq, digər hadisələrə növbə vermək
`Timer(d, fn)` / `Timer.run(fn)`Event queueZaman əsaslı iş
I/O, şəbəkə, isolate mesajıEvent queueXarici mənbələr — sən planlamırsan, sistem əlavə edir

`Future.microtask` vs `Future(() {})` vs `Future.delayed(Duration.zero)` — bu üçlük müsahibədə birlikdə soruşulur:

  • Future.microtask(fn) — microtask queue. Cari sinxron kod bitən kimi, hər hansı timer və I/O-dan əvvəl işləyir.
  • Future(fn) — event queue-ya "sıfır müddətli timer" kimi düşür. Bütün microtask-lardan sonra işləyir.
  • Future.delayed(Duration.zero, fn) — praktik olaraq Future(fn) ilə eynidir; hər ikisi timer yaradır. Fərq nüansdadır: Future.delayed açıq şəkildə Timer qurur və növbədə Future(fn)-dən sonra dayanır (hər ikisi eyni anda planlanıbsa).

Nə üçün bunu bilmək lazımdır? İki real səbəb:

1. Microtask starvation. Microtask-lar event queue-dan mütləq üstündür. Özünü yenidən planlayan microtask zənciri timer-ləri, I/O-nu və UI kadrlarını sonsuza qədər gözlədə bilər. Ona görə microtask-lar yalnız qısa işlər üçündür. 2. Bloklanma. Uzun sinxron hesablama heç bir növbəni işlətmir — event loop dayanır. Flutter-də bu, birbaşa donmuş UI-dır: 16 ms-lik kadr büdcəsini keçən hər sinxron iş kadr atlanmasına səbəb olur.

Diqqət: await Future.delayed(Duration.zero) "bir az gözləmək" demək deyil — o, sadəcə növbədə hazır olan işlərə icazə verir. Sənin öz sinxron hesablamanı yüngülləşdirmir.

İcra sırasını proqnozlaşdırmaq — lövhə sualının həlli. Sual belə səslənir: "bu kod nə çap edir?" Cavabı beş addımla, mexaniki şəkildə çıxar:

1. Sinxron sətirləri yuxarıdan aşağı yaz — hamısı birinci çap olunur. 2. Yol boyu qarşına çıxan Future.microtask, scheduleMicrotask və tamamlanmış future üzərindəki then-ləri microtask siyahısına planlanma sırası ilə yaz. 3. Future(...), Future.delayed(...), Timer(...) çağırışlarını event siyahısına yaz. 4. Sinxron hissə bitəndən sonra microtask siyahısını tam işlə (yeni əlavələr də bura düşür). 5. Sonra event siyahısını bir-bir işlə; hər elementdən sonra microtask siyahısını yenidən boşalt.

İki incəlik:

  • async funksiyanın birinci `await`-ə qədərki hissəsi sinxrondur — dərhal işləyir. await-dan sonrakı hissə isə callback kimi planlanır.
  • await someAlreadyCompletedFuture; yenə də funksiyanı dayandırır və qalanını microtask kimi planlayır — "artıq hazırdır, deməli sinxron davam edir" mülahizəsi səhvdir.

Müsahibə ipucu. Bu mövzuda sual demək olar həmişə icra sırası tapmacası şəklində gəlir. Güclü cavabın açarı — nəticəni təxmin etmək yox, prosesi səsli izah etmək: "əvvəlcə sinxron sətirlər... bunlar microtask növbəsinə düşür... bu isə timer-dir, deməli event queue-ya...". Müsahib məhz modeli axtarır, cavabı yox.

Bir cümlə mütləq deyilməlidir: "microtask queue tam boşalmayınca event queue-ya keçilmir". Bu, bütün tapmacaların açarıdır.

Ən çox edilən üç səhv: (1) await-in "proqramı dayandırdığını" düşünmək — dayanan yalnız həmin funksiyadır; (2) Future.delayed(Duration.zero)-nu microtask saymaq — o, timer-dir; (3) async funksiyanın birinci await-ə qədərki hissəsinin sinxron olduğunu unutmaq.

Sonda praktik nəticəni əlavə et — bu, səni fərqləndirir: bu bilik akademik deyil, çünki uzun sinxron hesablama Flutter-də birbaşa donmuş UI deməkdir, sonsuz microtask zənciri isə timer-ləri ac qoyur.

📚 Mənbələr və sənədlər