IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq4/32-dars20 daqiqa
Mundarija (33)

JavaScript event loop: stek, task va microtask navbatlari

Qisqacha: Event loop — call stack bo'shashini kutib, navbatdagi ishni stekka beradigan mexanizm. Bitta aylanishda: navbatdan bitta task (taymer, bosish) bajariladi, keyin microtask navbati to'liq bo'shatiladi, keyin brauzer kerak bo'lsa ekranni chizadi. Shuning uchun queueMicrotask callback'i setTimeout(fn, 0) dan oldin ishlaydi: 1, 2 (sinxron) → 3 (microtask) → 4 (taymer).

Bu darsda

  • Event loop'ning qismlarini — call stack, Web API, task navbati, microtask navbati — va ular orasidagi yo'lni chiza olasiz.
  • Taymer, microtask va sinxron kod aralash kodning chiqish tartibini oldindan, qadam-baqadam ayta olasiz.
  • Brauzer ekranni qachon chizishini va nega uzun ish paytida chizmasligini tushuntira olasiz.
  • Microtask navbati nega taymerlarni "och qoldirishi" mumkinligini bilasiz.
  • Node'ning event loop'i brauzernikidan nimasi bilan farq qilishini (setImmediate, process.nextTick) bilasiz.

Oldin bilishingiz kerak: Execution context va call stack, Sinxron va asinxron kod, Taymerlar, Callbacklar va callback hell.

1. Nega bu kerak?

Oxirgi darslarda bir nechta "sir" yig'ildi:

  • setTimeout(fn, 0) — 0 ms bo'lsa ham, joriy kod tugagandan keyin ishlaydi (Taymerlar).
  • 100 ms li taymer stek band bo'lgani uchun 1000 ms da ishladi (Sinxron va asinxron kod).
  • Sinxron kutish paytida "Kutilmoqda..." yozuvi ekranga umuman chiqmadi.
  • Closure tuzoqlari darsida var li sikldagi taymerlar uchala marta 4-urinish deb yozdi — "nega shunday, Event loop darsida ko'ramiz" degan edik.

Bularning hammasi bitta mexanizmdan kelib chiqadi. Bugun shu mexanizmni to'liq ochamiz. Undan keyin kodga qarab, natijalar qaysi tartibda chiqishini ishga tushirmasdan ayta olasiz — bu asinxron JavaScript'dagi eng muhim ko'nikma va intervyularning eng sevimli savoli.

«Bahor» oshxonasini davom ettiramiz. Execution context darsida Rustam aka ilgichdagi eng ustki varaq bo'yicha ishlardi — bu call stack edi. Endi oshxonaning qolgan qismini ko'ramiz: buyurtmalar qayerdan keladi va kim ularni ilgichga qo'yadi?

2. Event loop'ning qismlari

2.1 To'rtta joy

Asinxron kodda har bir callback to'rt joydan o'tishi mumkin:

Joy Nima «Bahor»da
Call stack hozir bajarilayotgan kod Rustam aka qo'lidagi varaq
Web API (host) kutayotgan ishlar: taymer, tarmoq, hodisa qozonlar, qum soatlari, eshik qo'ng'irog'i
Task navbati vaqti kelgan, stekni kutayotgan callback'lar kassadagi buyurtmalar navbati
Microtask navbati "joriy ish tugashi bilan" bajarilishi kerak bo'lgan kichik ishlar joriy taomga qo'shiladigan "yana bir narsa" ro'yxati

Uchtasi tanish. Call stack — Execution context dan. Web API va navbat — Sinxron va asinxron kod dagi vizualdan. Yangi ikkitasini aniqlaymiz.

Task (macrotask deb ham ataladi) — event loop bir aylanishda stekka beradigan bitta mustaqil ish: skriptning o'zi, taymer callback'i, bosish hodisasi tinglovchisi, tarmoqdan kelgan javob. Task navbati (task queue) — shunday ishlar kelish tartibida turadigan navbat. Navbat (queue) — birinchi kelgan birinchi ketadi, xuddi kassa oldidagi odamlar kabi.

Microtask — joriy task tugashi bilan, keyingi task'dan oldin bajarilishi shart bo'lgan kichik ish. Microtask navbati (microtask queue) — ular uchun alohida, "imtiyozli" navbat.

2.2 Event loop — bitta qoida

Event loop (hodisalar sikli) — host ichidagi cheksiz takrorlanuvchi jarayon. U doim bitta savolni beradi: "stek bo'shmi? Bo'sh bo'lsa — navbatdan keyingisini ber".

Aniqroq, bitta aylanish shunday (brauzer uchun):

  1. Task navbatidan bitta task olinadi va bajariladi — stek bo'shaguncha.
  2. Microtask navbati to'liq bo'shatiladi — hatto shu orada yangi microtask qo'shilsa ham.
  3. Vaqti kelgan bo'lsa, brauzer ekranni chizadi (render).
  4. 1-qadamga qaytiladi. Navbat bo'sh bo'lsa — yangi task kelishini kutadi.

Sxemada qarang. Strelkalar bitta aylanishni ko'rsatadi:

flowchart TD
  T["Navbatdan<br/>BITTA task"] --> S["Stekda bajarish<br/>(bo'shaguncha)"]
  S --> M{"Microtask<br/>navbati bo'shmi?"}
  M -- "yo'q" --> MB["Bitta microtask<br/>bajarish"]
  MB --> M
  M -- "ha" --> R{"Chizish<br/>vaqti keldimi?"}
  R -- "ha" --> C["Ekranni chizish"]
  R -- "yo'q" --> T
  C --> T

E'tibor bering: task'dan faqat bitta, microtask'dan esa hammasi. Bu farq bugungi hamma tartib savollarining kaliti.

Skriptning o'zi ham — birinchi task. Shuning uchun sinxron kod har doim birinchi va to'liq bajariladi: u tugamaguncha stek bo'shamaydi, event loop esa hech narsa bera olmaydi.

Tekshirib ko'ring: Event loop stekda biror funksiya bajarilayotganda uni to'xtatib, navbatdagi taymer callback'ini bera oladimi?

Javob

Yo'q. Event loop faqat stek bo'sh bo'lganda navbatdagini beradi. Bajarilayotgan kodni hech kim to'xtatmaydi — u oxirigacha ishlaydi. Shuning uchun uzun sinxron kod hamma taymer va bosishlarni kechiktiradi.

3. queueMicrotask: birinchi microtask

3.1 Microtask qo'yish

Microtask navbatiga ish qo'yishning eng to'g'ridan-to'g'ri yo'li — queueMicrotask(fn). U fn ni microtask navbatiga qo'yadi va darhol qaytadi. Brauzerda ham, Node'da ham bor.

Microtask'ning asosiy manbai esa boshqa — Promise. Promise natijasini kutadigan callback'lar aynan microtask navbatiga tushadi. Uni keyingi darsda, Promise asoslari da boshlaymiz — hozir bilish shart emas. Bugun bitta queueMicrotask bilan mexanizmni tushunsangiz, Promise tartibi ham o'z-o'zidan ochiladi.

3.2 Klassik savol

Kod, taymer va microtask aralash:

js
console.log("1. Skript boshlandi");

setTimeout(() => {
  console.log("4. Taymer");
}, 0);

queueMicrotask(() => {
  console.log("3. Microtask");
});

console.log("2. Skript tugadi");

Konsolda:

text
1. Skript boshlandi
2. Skript tugadi
3. Microtask
4. Taymer

Taymer kodda microtask'dan oldin yozilgan va 0 ms. Baribir microtask oldin chiqdi. Qadamma-qadam qarang. O'ng tomonda uch navbat-joy: Web API, Task navbati va Microtask navbati:

Qoidani bir gapda: sinxron kod → hamma microtask'lar → bitta task → hamma microtask'lar → bitta task ...

Tekshirib ko'ring: Kodda ikkita setTimeout(fn, 0) va ulardan keyin bitta queueMicrotask bo'lsa, microtask nechanchi bo'lib ishlaydi (sinxron koddan keyin)?

Javob

Birinchi. Ikkala taymer callback'i task navbatida, microtask esa microtask navbatida. Skript tugashi bilan avval microtask navbati bo'shatiladi, keyin taymerlar birin-ketin — har biri alohida aylanishda.

4. Microtask navbati to'liq bo'shatiladi

4.1 Microtask ichidan microtask

Event loop qoidasining ikkinchi qadamini eslang: "microtask navbati to'liq bo'shatiladi — hatto shu orada yangisi qo'shilsa ham". Buni sinaymiz. Microtask ichidan yana microtask va yana taymer qo'yamiz. Ishga tushirishdan oldin tartibni o'zingiz ayting:

js
console.log("1");
setTimeout(() => console.log("2: taymer"), 0);
queueMicrotask(() => {
  console.log("3: microtask");
  queueMicrotask(() => console.log("4: microtask ichidan"));
  setTimeout(() => console.log("5: microtask ichidan taymer"), 0);
});
console.log("6");

Konsolda:

text
1
6
3: microtask
4: microtask ichidan
2: taymer
5: microtask ichidan taymer

Qadamlarda kuzating — ayniqsa "4" ning navbatga qachon qo'shilib, qachon chiqishiga:

Ikki xulosa:

  • «4» — microtask ichida qo'yilgan bo'lsa ham — taymer «2» dan oldin chiqdi. Microtask navbati bo'shamaguncha task'ga navbat kelmaydi.
  • «5» — «2» dan keyin. Ikkalasi task navbatida, «5» keyin qo'yilgan. Navbat — birinchi kelgan birinchi ketadi.

4.2 Microtask'lar taymerni "och qoldiradi"

Microtask o'zidan keyin yana microtask qo'yaversa nima bo'ladi? Taymer kutaveradi:

js
setTimeout(() => {
  console.log("Taymer keldi");
}, 0);

let soni = 0;
function kichikIsh() {
  soni++;
  if (soni < 1_000_000) {
    queueMicrotask(kichikIsh);
  } else {
    console.log("Microtask'lar tugadi:", soni);
  }
}

queueMicrotask(kichikIsh);
console.log("Sinxron kod tugadi");

Konsolda:

text
Sinxron kod tugadi
Microtask'lar tugadi: 1000000
Taymer keldi

0 ms li taymer million microtask tugashini kutdi. Agar if sharti bo'lmasa — microtask'lar abadiy bir-birini qo'yaverardi. Taymer hech qachon ishlamasdi, brauzerda esa sahifa butunlay qotardi: chizish ham microtask'lardan keyin keladi.

Taqqoslang: xuddi shunday cheksiz zanjirni setTimeout(kichikIsh, 0) bilan qilsangiz, sahifa qotmaydi. Har taymer — alohida task. Ularning orasida event loop bosishlarni ham, chizishni ham bajarishga ulguradi. Bu — microtask va task'ning eng amaliy farqi.

Diqqat: Microtask — "tezroq setTimeout" emas. U joriy task'ning davomi: hammasi tugaguncha brauzer hech narsa qila olmaydi. Uzun ishni microtask'larga bo'lish sahifani qutqarmaydi — buning uchun task'lar (taymer) kerak.

4.3 Tartibni oldindan aytish: to'rt qadam

Endi har qanday aralash kod uchun tartibni qog'ozda topa olasiz. Har safar bir xil yo'l bilan:

  1. Sinxron qatorlarni belgilang — ular kod tartibida, hammasidan oldin chiqadi.
  2. Har callback qaysi navbatga tushishini yozing: queueMicrotask — microtask, setTimeout/setInterval — task (vaqti kelganda), hodisa — task (sodir bo'lganda).
  3. Skript tugagach — microtask navbatini oxirigacha "bajaring". Ular ichida yangi callback'lar qo'yilsa, ularni ham tegishli navbatning oxiriga yozing.
  4. Task navbatidan bittasini oling, bajaring, yana 3-qadamga qayting.

Kechikishi har xil taymerlar task navbatiga vaqti kelganda tushadi: 100 ms li taymer 0 ms lidan keyin, qaysi biri oldin yozilganidan qat'i nazar.

4.4 var li sikl siri

Endi Closure tuzoqlari darsidagi va'dani bajaramiz. U yerdagi kod:

js
for (var urinish = 1; urinish <= 3; urinish++) {
  setTimeout(() => console.log(`${urinish}-urinish`), 0);
}

Konsolda:

text
4-urinish
4-urinish
4-urinish

To'rt qadam bilan yechamiz. Sikl — sinxron kod: u uchta taymer qo'yadi va tugaydi. Sikl tugaganda urinish — 4 (shart 4 <= 3 bajarilmagani uchun to'xtadi). Uchala callback task navbatida kutadi va faqat skript tugagandan keyin ishlaydi. Ular ishlaganda var urinish — bitta umumiy o'zgaruvchi — allaqachon 4. let bilan esa har aylanishda yangi o'zgaruvchi bo'ladi va callback'lar 1, 2, 3 ni ko'radi. Closure qaysi o'zgaruvchini eslashini — Closure darslari, qachon ishlashini esa event loop tushuntiradi.

5. Chizish qadami

5.1 Brauzer qachon chizadi?

Chizish (render) — brauzer DOM o'zgarishlarini hisoblab, ekranga piksellar sifatida chiqarishi. Ekran odatda soniyada 60 marta yangilanadi — har 16,7 ms da bir imkoniyat. Event loop bu imkoniyatni faqat task va uning microtask'lari tugagandan keyin ishlatadi.

Demak siz bitta tinglovchida DOM'ni o'n marta o'zgartirsangiz, foydalanuvchi faqat oxirgisini ko'radi. Sinxron va asinxron kod darsidagi "Kutilmoqda..." shuning uchun ko'rinmagan edi.

Ikki tugmani sinang. Ikkalasi ham 1 dan 5 gacha sanaydi va har raqamda 300 ms ish qiladi. «Bitta task» hammasini bitta tinglovchida bajaradi. «Har qadam — alohida task» har raqamni alohida setTimeout da bajaradi:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  .raqam { font: 700 3rem/1.2 ui-monospace, monospace; margin: 0; }
  button { font: inherit; padding: 0.4rem 0.8rem; margin: 0.25rem 0; }
</style>
<p class="raqam" id="raqam">0</p>
<button id="bitta" type="button">Bitta task</button>
<button id="alohida" type="button">Har qadam — alohida task</button>
<script>
  const raqam = document.getElementById("raqam");

  function bandQil(ms) {
    const boshi = Date.now();
    while (Date.now() - boshi < ms) {}
  }

  document.getElementById("bitta").addEventListener("click", () => {
    for (let i = 1; i <= 5; i++) {
      raqam.textContent = i;
      bandQil(300);
    }
    console.log("Bitta task: tugadi");
  });

  function qadam(i) {
    raqam.textContent = i;
    bandQil(300);
    if (i < 5) {
      setTimeout(qadam, 0, i + 1);
    } else {
      console.log("Alohida task'lar: tugadi");
    }
  }

  document.getElementById("alohida").addEventListener("click", () => {
    qadam(1);
  });
</script>

Nimani ko'rdingiz:

  • «Bitta task» — 1,5 soniya hech narsa o'zgarmaydi, keyin birdan 5 chiqadi. 1, 2, 3, 4 ham textContent ga yozildi, lekin chizilmadi: stek bo'shamadi.
  • «Har qadam — alohida task» — raqamlar birin-ketin almashadi. Har qadam alohida task, ular orasida brauzer chizishga ulguradi.

Ikkala holatda ham umumiy ish bir xil — 1,5 soniya. Lekin ikkinchisida foydalanuvchi jarayonni ko'radi va sahifa har 300 ms da "nafas oladi" — bosishlar ham shu oraliqlarda bajariladi. Og'ir ishni task'larga bo'laklash — bloklashga qarshi asosiy usullardan biri. Uning to'liq ko'rinishini 3-mashqda yozasiz.

5.2 requestAnimationFrame haqida bir gap

Chizishdan oldin, aynan har kadr uchun ishlaydigan callback ham bor — requestAnimationFrame. U animatsiya uchun mo'ljallangan va 13-qismda, requestAnimationFrame darsida o'rganiladi — hozir bilish shart emas. Shuni biling: u ham event loop'ning bir qismi, chizish qadamiga biriktirilgan.

Tekshirib ko'ring: Tinglovchi DOM'ga 1000 ta element qo'shdi, keyin queueMicrotask bilan yana bittasini. Foydalanuvchi ekranda avval 1000 tasini, keyin 1001 tasini ko'radimi?

Javob

Yo'q — darhol 1001 tasini ko'radi. Microtask task tugashi bilan, chizishdan oldin bajariladi. Brauzer birinchi marta chizganda DOM'da allaqachon 1001 ta element bor.

6. Node'da event loop

6.1 Umumiy qism bir xil

Node'da ham call stack bitta, ham task'lar va microtask'lar bor, ham "microtask navbati to'liq bo'shatiladi" qoidasi ishlaydi. Shu darsdagi js misollar Node 24 da tekshirilgan va brauzerda ham xuddi shu tartibni beradi. Farqlar — task'larning ichki tartibida va Node'ning o'z qo'shimcha navbatlarida. Ekranni chizish qadami esa Node'da yo'q — ekran yo'q.

6.2 setImmediate va fazalar

Node'ning event loop'i bitta task navbati emas, bir nechta faza (bosqich) dan iborat: taymerlar, kirish-chiqish — inglizcha I/O (input/output: fayl, tarmoq) — callback'lari, setImmediate va boshqalar. Har aylanishda fazalar ma'lum tartibda o'tiladi. Fazalarni Event loop fazalari chuqur darsida, 24-qismda ko'ramiz.

setImmediate(fn) — faqat Node'da: "kirish-chiqish fazasidan keyin, darhol". Fayl o'qilgandan keyin taymer va setImmediate qo'ysak, setImmediate doim birinchi:

js
import { readFile } from "node:fs";

readFile(import.meta.filename, () => {
  setTimeout(() => console.log("setTimeout 0"), 0);
  setImmediate(() => console.log("setImmediate"));
});

Konsolda:

text
setImmediate
setTimeout 0

import.meta.filename — shu faylning o'z yo'li: fayl o'zini o'qiydi, shunchaki o'qiladigan biror fayl kerak edi. Fayl callback'idan keyin event loop setImmediate fazasiga keladi, taymerlar fazasi esa keyingi aylanishda. Faylning yuqori darajasida esa bu ikkisining tartibi kafolatlanmaydi — kompyuter tezligiga qarab o'zgaradi. Bunday tartibga tayanmang.

6.3 process.nextTick

process.nextTick(fn) — Node'ning yana bir navbati, microtask'larga o'xshash, lekin alohida. Oddiy .cjs faylda u queueMicrotask dan ham oldin ishlaydi. ESM (.mjs) faylning yuqori darajasida esa — keyin. Bu nozik farq va uning sababi — Microtask navbati, process.nextTick va setImmediate darsida. Bugun bitta qoida: yangi kodda queueMicrotask ni tanlang — u brauzerda ham, Node'da ham bir xil ishlaydi.

Brauzer Node
Task'lar bitta (asosiy) navbat fazalar: taymerlar, I/O, setImmediate ...
Microtask queueMicrotask, Promise queueMicrotask, Promise + process.nextTick
Chizish bor yo'q
setTimeout(fn, 0) 0 ms, ichma-ich — kamida 4 ms 1 ms

Tekshirib ko'ring: Node'da faylning eng yuqorisida (hech qanday callback ichida emas) setTimeout(fn, 0) va setImmediate(fn) yozildi. Qaysi biri birinchi ishlaydi?

Javob

Oldindan aytib bo'lmaydi — tartib kafolatlanmagan. U event loop qaysi fazadan boshlashiga va kompyuter tezligiga bog'liq. setImmediate doim birinchi bo'lishi faqat kirish-chiqish (I/O) callback'i ichida, masalan readFile callback'ida kafolatlangan.

7. Loupe: event loop'ni jonli ko'ring

Philip Roberts 2014 yilda event loop haqida mashhur chiqish qilgan ("What the heck is the event loop anyway?", JSConf EU) va uning uchun vizualizator yozgan — Loupe: latentflip.com/loupe. Unga kod yozasiz, u esa stek, Web API'lar va task navbati orasidagi harakatni sekinlashtirib ko'rsatadi. Tugma bosish hodisalarini ham sinash mumkin.

Ikki cheklovini biling: Loupe eski vosita — microtask navbatini ko'rsatmaydi, zamonaviy sintaksis bilan esa ba'zan ishlamaydi. Xato bersa, arrow funksiyalar o'rniga function yozing. Bu darsdagi birinchi vizualning queueMicrotask siz qismini Loupe'da ishga tushirib, taymer callback'i yo'lini kuzating.

8. Ko'p uchraydigan xatolar

8.1 Tartibni kod tartibidan taxmin qilish

"setTimeout yuqorida yozilgan — demak birinchi chiqadi". Yo'q: tartibni navbat turi belgilaydi. Avval sinxron kod, keyin microtask'lar, keyin task'lar — har biri o'z navbatida kelish tartibida. Tuzatish: murakkab kodda har callback yoniga "S", "M" yoki "T" deb belgi qo'yib, keyin tartiblang — 1-mashqda shunday qilasiz.

8.2 setTimeout(fn, 0) va queueMicrotask(fn) ni bir xil deb o'ylash

Ikkalasi ham "joriy koddan keyin". Lekin microtask — shu task'ning oxirida, chizishdan oldin; taymer — keyingi task'larning birida, ehtimol chizishdan keyin. Foydalanuvchi oraliq holatni ko'rishi kerak bo'lsa — taymer. Hech qanday oraliq holat ko'rinmasligi kerak bo'lsa — microtask.

8.3 Cheksiz microtask

O'zini qayta qo'yadigan microtask — cheksiz sikl bilan bir xil: taymerlar, bosishlar va chizish abadiy kutadi. Brauzer bir necha soniyadan keyin «Page Unresponsive» oynasini chiqaradi. Tuzatish: takrorlanuvchi ish uchun taymer ishlating yoki to'xtash shartini tekshiring.

8.4 Og'ir ishni bitta setTimeout ga o'rash

setTimeout(ogirHisob, 0) — sahifani qutqarmaydi, faqat qotishni bir lahzaga kechiktiradi. Task baribir 2 soniya stekni egallaydi. Tuzatish: ishni bo'laklarga bo'ling — har bo'lak alohida task (3-mashq).

9. Mashqlar

1-mashq (oson): S, M yoki T?

Har console.log yoniga "S" (sinxron), "M" (microtask) yoki "T" (taymer task'i) deb belgi qo'ying va chiqish tartibini yozing. Keyin tekshiring.

js
setTimeout(() => console.log("A"), 0);
console.log("B");
queueMicrotask(() => console.log("C"));
setTimeout(() => console.log("D"), 0);
queueMicrotask(() => console.log("E"));
console.log("F");
Yechim

A — T, B — S, C — M, D — T, E — M, F — S.

Konsolda:

text
B
F
C
E
A
D

Avval sinxronlar (B, F) kod tartibida. Keyin microtask navbati to'liq: C, E. Keyin task'lar — har biri alohida aylanishda, qo'yilgan tartibda: A, D.

2-mashq (o'rta): Taymer ichida microtask

Tartibni oldindan ayting. Ishora: har taymer callback'i — alohida task, va har task'dan keyin microtask navbati bo'shatiladi.

js
setTimeout(() => {
  console.log("1-taymer");
  queueMicrotask(() => console.log("1-taymerning microtask'i"));
}, 0);

setTimeout(() => {
  console.log("2-taymer");
}, 0);

queueMicrotask(() => {
  console.log("microtask");
  setTimeout(() => console.log("3-taymer"), 0);
});
Yechim

Konsolda:

text
microtask
1-taymer
1-taymerning microtask'i
2-taymer
3-taymer

Sinxron kod hech narsa chiqarmaydi, faqat navbatlarga qo'yadi. Skript tugagach — microtask: u "3-taymer" ni task navbatining oxiriga qo'shadi. Keyin 1-taymer — uning microtask'i shu task tugashi bilan, 2-taymerdan oldin ishlaydi. Keyin 2-taymer, oxirida 3-taymer.

3-mashq (qiyin): Og'ir ishni bo'laklash

Rustam aka oy oxirida 3 000 000 ta chekning summasini hisoblamoqchi (har chek — i % 100 so'm, ya'ni 0 dan 99 gacha). Bitta siklda hisoblasa, shu orada kelgan buyurtma kutib qoladi. bolaklabHisobla(jami, bolak, tugadi) funksiyasini yozing: u har safar bolak ta chekni hisoblasin va keyingi bo'lakni setTimeout(..., 0) bilan rejalashtirsin. Oxirida tugadi(summa) chaqirilsin.

Sinov: jami = 3_000_000, bolak = 1_000_000. Hisob boshlanishidan oldin setTimeout(() => console.log("Yangi buyurtma qabul qilindi"), 0) qo'ying — u hisob oxirida emas, birinchi bo'lakdan keyin chiqishi kerak. Faylni kurs/mashqlar/11/04-event-loop/bolaklash.mjs ga saqlang.

Yechim
js
function bolaklabHisobla(jami, bolak, tugadi) {
  let summa = 0;
  let i = 0;
  let tartib = 0;

  function keyingiBolak() {
    const chegara = Math.min(i + bolak, jami);
    for (; i < chegara; i++) {
      summa += i % 100;
    }
    tartib++;
    console.log(`${tartib}-bo'lak tugadi`);
    if (i < jami) {
      setTimeout(keyingiBolak, 0);
    } else {
      tugadi(summa);
    }
  }

  keyingiBolak();
}

setTimeout(() => console.log("Yangi buyurtma qabul qilindi"), 0);

bolaklabHisobla(3_000_000, 1_000_000, (summa) => {
  console.log("Jami summa:", summa);
});

Konsolda:

text
1-bo'lak tugadi
Yangi buyurtma qabul qilindi
2-bo'lak tugadi
3-bo'lak tugadi
Jami summa: 148500000

Birinchi bo'lak sinxron bajarildi (skript task'ining ichida). Keyin task navbatida ikki ish bor edi: avval qo'yilgan "Yangi buyurtma" va keyin qo'yilgan 2-bo'lak. Navbat tartibi bo'yicha buyurtma birinchi o'tdi. Bitta siklda hisoblaganda esa u eng oxirida chiqardi.

for (; i < chegara; i++) — for ning birinchi qismi bo'sh: i sikldan tashqarida e'lon qilingan, chunki u bo'laklar orasida saqlanishi kerak (closure). Summani tekshirish: har 100 ta chekda 0+1+...+99 = 4950, 3 000 000 da esa 30 000 × 4950 = 148 500 000.

bash
git add 11/04-event-loop/bolaklash.mjs
git commit -m "11/04: og'ir hisobni bo'laklarga bo'l"

10. Real ishda

  • Intervyu. "Bu kod nimani qaysi tartibda chiqaradi?" — Promise, setTimeout va async aralash kod bilan — frontend va Node intervyularining eng ko'p uchraydigan amaliy savoli. Bugungi qoida ("sinxron → hamma microtask → bitta task") Promise bilan ham aynan shunday ishlaydi.
  • Sezgir interfeys. React, Vue kabi freymvorklar DOM yangilanishlarini bitta microtask yoki kadrga yig'ib, bir marta chizadi — ular bugungi chizish qoidasiga tayanadi. React 17-qismda; hozir bilish shart emas.
  • Og'ir ish. Katta jadvallarni filtrlash, fayllarni tahlil qilish — bo'laklash yoki alohida thread (Web Workers). Brauzerlarda bo'laklash uchun maxsus scheduler.yield() ham paydo bo'lmoqda — 13-qismda.
  • Node serverlar. Bitta so'rov uchun og'ir sinxron hisob — hamma foydalanuvchilar uchun to'xtash. Node'chilar buni "event loop'ni bloklama" deb aytishadi; event loop kechikishini o'lchaydigan monitoring vositalari bor (Blocking va non-blocking I/O).

Xulosa

  • Event loop — stek bo'shaganda navbatdagini beradigan mexanizm; ishlayotgan kodni hech qachon to'xtatmaydi.
  • Bir aylanish: bitta task → hamma microtask'lar (yangi qo'shilganlari ham) → kerak bo'lsa chizish.
  • Task'lar: skript, taymerlar, hodisalar, tarmoq javoblari. Microtask'lar: queueMicrotask, Promise callback'lari.
  • Tartib qoidasi: sinxron kod → microtask'lar → task'lar, har navbat ichida — kelish tartibida.
  • Brauzer faqat task va microtask'lar tugagach chizadi; cheksiz microtask sahifani butunlay qotiradi.
  • Node'da qo'shimcha: fazalar, setImmediate, process.nextTick; chizish yo'q.

Keyingi dars: Promise asoslari — callback'lar o'rniga "kelajakdagi natija" obyekti: kutilmoqda, bajarildi va rad etildi holatlari, then va catch — va ular nega microtask navbatidan foydalanadi.

Manbalar

  • HTML Living Standard: "Event loops" (processing model, microtask checkpoint, update the rendering) — html.spec.whatwg.org
  • MDN: "Using microtasks in JavaScript with queueMicrotask()", "The event loop" — developer.mozilla.org
  • Jake Archibald, "Tasks, microtasks, queues and schedules" — jakearchibald.com
  • Node.js: "The Node.js Event Loop" — nodejs.org/learn
  • Philip Roberts, "What the heck is the event loop anyway?" (JSConf EU 2014) va Loupe — latentflip.com/loupe
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript event loop: stek, task va microtask navbatlari — IlmHamroh