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

Sinxron va asinxron JavaScript: nega kod kutib o'tirmasligi kerak

Qisqacha: Sinxron kod — har qator oldingisi tugashini kutib bajariladi. JavaScript'da call stack bitta, shuning uchun uzoq sinxron ish (masalan, 2 soniyalik sikl) shu vaqt ichida boshqa hamma narsani to'xtatadi: sahifa qotadi. Asinxron kod esa kutishni brauzer yoki Node'ga topshiradi va darhol davom etadi: setTimeout(fn, 1000) dan keyingi qator kutmasdan ishlaydi, fn esa keyinroq chaqiriladi.

Bu darsda

  • Sinxron va asinxron kodni o'z so'zingiz bilan, misol bilan farqlay olasiz.
  • JavaScript nega bir threadli ekanini va bu nimaga olib kelishini tushuntira olasiz.
  • Bloklash sahifani qanday qotirishini o'z ko'zingiz bilan ko'rasiz va Node'da o'lchaysiz.
  • Kutishni kim bajarishini bilasiz: JavaScript emas, uni ishga tushirgan host — brauzer yoki Node.
  • Callback darhol chaqiriladimi yoki keyinroqmi — kodga qarab ayta olasiz.

Oldin bilishingiz kerak: Funksiya — qiymat: callback va HOF, Execution context va call stack, addEventListener asoslari, Closure amaliyotda.

1. Nega bu kerak?

«Bahor» oshxonasida bitta ofitsiant bor — Otabek. Dilshod aka 5-stolda osh buyurtma qildi. Osh 20 daqiqada tayyor bo'ladi.

Otabek ikki xil ishlashi mumkin:

  1. Buyurtmani oshxonaga beradi va oshxona eshigi oldida 20 daqiqa turadi. Bu orada boshqa stollar chaqiradi, choy so'raydi, hisob kutadi — hech kim javob olmaydi.
  2. Buyurtmani Rustam akaga beradi va darhol boshqa stollarga qaytadi. Osh tayyor bo'lganda Rustam aka qo'ng'iroq chaladi. Otabek qo'li bo'shashi bilan oshni olib boradi.

Birinchi usul — sinxron ishlash. Ikkinchisi — asinxron. Ikkalasida ham osh 20 daqiqada pishadi. Farq — shu 20 daqiqada boshqalar nima qiladi.

JavaScript'da Otabek bitta. Execution context va call stack darsining oxirida buni ko'rgan edik: call stack bitta, bir vaqtda faqat bitta ish bajariladi. Sahifadagi hamma narsa — tugmalar, matn yozish, animatsiya — shu bitta "ofitsiant"ga qaraydi. U eshik oldida turib qolsa, sahifa qotadi.

11-qism shu muammo haqida. Taymerlar, serverdan ma'lumot olish (fetch), brauzer xotirasi — hammasi "kutish"ni o'z ichiga oladi. Bugun eng asosiy savolga javob beramiz: kutish paytida JavaScript nima qiladi?

2. Sinxron kod: navbat bilan, birin-ketin

2.1 Siz yozib kelgan kod

Sinxron (synchronous) kod — har qator oldingisi to'liq tugagandan keyin bajariladigan kod. "Sinxron" so'zi "bir vaqtda, birga qadam tashlab" degan ma'noni beradi: kod yozilgan tartib bilan bajarilish tartibi bir xil.

08-qismdan beri yozgan deyarli hamma kodingiz shunday:

js
console.log("Buyurtma qabul qilindi");
const jami = 35000 * 2 + 5000;
console.log(`Hisob: ${jami} so'm`);
console.log("Chek chiqarildi");

Konsolda:

text
Buyurtma qabul qilindi
Hisob: 75000 so'm
Chek chiqarildi

Ikkinchi qator hisoblanmaguncha, uchinchisi boshlanmaydi. Funksiya chaqirilsa, JavaScript uni oxirigacha bajaradi va keyin chaqirilgan joyga qaytadi. Bu tartib ishonchli va tushunarli: kodni yuqoridan pastga o'qisangiz, nima qachon bo'lishini aniq bilasiz.

2.2 Sinxron callback'lar ham bor

Callback va HOF darsida callback — boshqa funksiyaga berilgan funksiya ekanini o'rgandik. 10-qismdagi forEach, map, filter callback'lari ham sinxron:

js
const taomlar = ["osh", "manti"];

console.log("Boshlandi");
taomlar.forEach((taom) => console.log(`- ${taom}`));
console.log("Tugadi");

Konsolda:

text
Boshlandi
- osh
- manti
Tugadi

forEach callback'ni o'zi ishlayotgan paytda ikki marta chaqirdi va shundan keyingina qaytdi. "Tugadi" har doim ro'yxatdan keyin chiqadi.

Tekshirib ko'ring: [3, 1, 2].toSorted((a, b) => a - b) dagi taqqoslash callback'i sinxronmi?

Javob

Ha. toSorted taqqoslash funksiyasini saralash paytida, o'zi ishlayotganda chaqiradi va saralangan massivni qaytargandan keyin uni boshqa hech qachon chaqirmaydi. Massiv metodlarining hamma callback'lari sinxron.

3. Bitta thread va bloklash

3.1 Bir threadli til

Thread (bajarilish ipi) — kompyuterda buyruqlarni birin-ketin bajaradigan bitta "ishchi". «Bahor» misolida thread — Otabekning o'zi. Kompyuterning protsessori ko'p thread'ni bir vaqtda yurita oladi. Lekin sizning JavaScript kodingiz — brauzerdagi sahifa skripti ham, Node'dagi fayl ham — bitta thread'da ishlaydi. Unga bitta call stack tegishli.

Bir threadli (single-threaded) — bir vaqtda faqat bitta ketma-ketlikni bajaradigan. Bu g'oyani Execution context darsida ko'rgan edik. Endi uni amalda sinaymiz.

Bu ongli tanlov. Sahifadagi elementlarni ikki thread bir vaqtda o'zgartirsa, chalkashlik boshlanardi: biri tugmani o'chirsa, ikkinchisi uning matnini yozmoqchi bo'lsa-chi? Bitta thread bunday to'qnashuvlardan qutqaradi. Narxi esa — bitta "ofitsiant" band bo'lsa, hamma kutadi.

3.2 Bloklash

Bloklash (blocking) — uzoq davom etadigan sinxron ish call stack'ni egallab, boshqa hamma ishni to'xtatib qo'yishi.

Uni Node'da o'lchaymiz. Quyidagi bandQil funksiyasi hech narsa qilmaydi — faqat berilgan millisekund o'tguncha siklda aylanadi. Undagi Date.now() — hozirgi vaqt millisekundlarda (Sahifa hayot sikli darsida ishlatgan edik). Taymer funksiyasi setTimeout ni Callback va HOF darsidan bilasiz: u funksiyani ko'rsatilgan vaqtdan keyin chaqiradi.

js
function bandQil(ms) {
  const boshi = Date.now();
  while (Date.now() - boshi < ms) {
    // hech narsa qilmaymiz — faqat vaqt o'tkazamiz
  }
}

const boshi = Date.now();
setTimeout(() => {
  const otdi = Date.now() - boshi;
  console.log(`Taymer ishladi: ${Math.round(otdi / 100) * 100} ms`);
}, 100);

bandQil(1000);
console.log("Og'ir ish tugadi");

Konsolda:

text
Og'ir ish tugadi
Taymer ishladi: 1000 ms

Taymerga 100 ms aytdik. U esa 1000 ms dan keyin ishladi. 100 ms o'tganda vaqt keldi — lekin call stack'da bandQil turgan edi. Taymer callback'i stek bo'shashini 900 ms kutdi.

O'tgan vaqtni 100 ga yaxlitladik. Aniq son har ishga tushirishda biroz farq qiladi (1003, 1006 ...) — kompyuter shu paytda boshqa ishlar bilan ham band. Yaxlitlangani esa doim bir xil.

Diqqat: bandQil — faqat namoyish uchun. Real kodda "kutish" uchun hech qachon bo'sh sikl yozmang: u protsessorni behuda qizdiradi va hamma narsani to'xtatadi. Kutishning to'g'ri yo'li — keyingi bo'limda.

3.3 Brauzerda: qotgan sahifa

Node'da bloklash — kechikkan taymer. Brauzerda esa uni ko'z bilan ko'rasiz. Quyida ikki tugma bor. Ikkalasi ham 2 soniya kutadi. Avval maydonga biror narsa yozing, keyin «Sinxron kutish» ni bosing va darhol yana yozishga urining. Keyin xuddi shuni «Asinxron kutish» bilan qiling.

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  input, button { font: inherit; padding: 0.4rem 0.6rem; }
  p { margin: 0.6rem 0; }
</style>
<label for="izoh">Izoh yozing:</label>
<input id="izoh" placeholder="Masalan: piyozsiz">
<p>
  <button id="sinxron" type="button">Sinxron kutish (2 s)</button>
  <button id="asinxron" type="button">Asinxron kutish (2 s)</button>
</p>
<p id="holat" role="status">Tayyor.</p>
<script>
  const holat = document.getElementById("holat");

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

  const sinxron = document.getElementById("sinxron");
  const asinxron = document.getElementById("asinxron");

  sinxron.addEventListener("click", () => {
    holat.textContent = "Kutilmoqda...";
    bandQil(2000);
    holat.textContent = "Sinxron kutish tugadi.";
    console.log("sinxron: tugadi");
  });

  asinxron.addEventListener("click", () => {
    holat.textContent = "Kutilmoqda...";
    setTimeout(() => {
      holat.textContent = "Asinxron kutish tugadi.";
      console.log("asinxron: tugadi");
    }, 2000);
  });
</script>

Nimani ko'rdingiz:

  • Sinxron tugmadan keyin 2 soniya maydonga hech narsa yozilmadi. Bosgan harflaringiz yo'qolmadi — ular kutib turdi va qotish tugagach birdaniga chiqdi. Bizning sinovimizda butun dars sahifasi ham 2 soniya qotdi: natija oynasi sahifa bilan bitta thread'da ishlaydi.
  • "Kutilmoqda..." yozuvi umuman ko'rinmadi. Brauzer ekranni faqat stek bo'shaganda qayta chiza oladi. Stek bo'shaganda esa matn allaqachon "Sinxron kutish tugadi." edi.
  • Asinxron tugmadan keyin "Kutilmoqda..." darhol ko'rindi va siz bemalol yoza oldingiz. 2 soniyadan keyin matn o'zgardi.

Haqiqiy saytda bloklash bir necha soniyadan oshsa, Chrome «Page Unresponsive» (sahifa javob bermayapti) oynasini chiqaradi va foydalanuvchiga sahifani yopishni taklif qiladi. Foydalanuvchi uchun bu "sayt buzildi" degani.

Tekshirib ko'ring: Nega «Sinxron kutish» da "Kutilmoqda..." matni ekranga chiqmadi, axir u bandQil dan oldin yozilgan-ku?

Javob

Matn holat elementiga haqiqatan yozildi. Lekin uni ekranga chizish — brauzerning alohida ishi va u faqat call stack bo'shaganda bajariladi. Tinglovchi tugaguncha stek band edi; tugaganda matn allaqachon ikkinchi marta o'zgargan edi. Foydalanuvchi faqat oxirgi holatni ko'rdi.

4. Asinxron kod: "keyinroq bajariladi"

4.1 Kutishni topshirish

Asinxron (asynchronous) kod — ishni boshlab qo'yadigan, lekin uning tugashini kutmasdan davom etadigan kod. Natija tayyor bo'lganda esa oldindan berilgan funksiya — callback — chaqiriladi. "Asinxron" — "bir vaqtda emas": kod yozilgan tartib bilan bajarilish tartibi farq qilishi mumkin.

08-qismdagi misolni eslang, endi «Bahor» ko'rinishida:

js
console.log("Buyurtma oshxonaga berildi");

setTimeout(() => {
  console.log("Osh tayyor — 5-stolga!");
}, 1000);

console.log("Otabek 3-stolga choy olib bordi");

Konsolda:

text
Buyurtma oshxonaga berildi
Otabek 3-stolga choy olib bordi
Osh tayyor — 5-stolga!

setTimeout kodda o'rtada turibdi, lekin uning callback'i oxirida ishladi. setTimeout qatori kutmadi: u taymerni ro'yxatga oldi va bir zumda qaytdi. Keyingi qator darhol bajarildi. Bir soniyadan keyin callback chaqirildi.

4.2 Qadamma-qadam: kutish qayerda bo'ladi?

Bir savol tug'iladi: JavaScript bitta thread'da ishlasa, 1000 ms ni kim sanadi? Call stack'da hech kim turmagan-ku.

Javob — JavaScript'ning o'zi sanamaydi. Taymerni brauzer (yoki Node) sanaydi. Quyidagi vizualda o'ngdagi bo'limga qarang. Bu safar unda o'zgaruvchilar emas, ikki joy ko'rsatilgan:

  • Web API — brauzerning kutish xizmatlari: taymer shu yerda sanaladi;
  • Navbat — vaqti kelgan, stekka chiqishni kutayotgan callback'lar.

Uchta narsaga e'tibor bering:

  • Kutish stekda bo'lmadi. 1000 ms davomida stekda hech kim yo'q edi — JavaScript boshqa ishni bajarishga tayyor turdi.
  • Callback "o'zi" stekka sakramaydi. Vaqti kelganda u avval navbatga tushadi va stek bo'shashini kutadi. Bloklash misolidagi 900 ms kechikish shu yerdan.
  • Callback stekka chiqqach — oddiy funksiya. Uning ichidagi kod yana sinxron bajariladi.

Navbat, stek va brauzer orasidagi bu harakatni boshqaradigan mexanizmning nomi — event loop (hodisalar sikli). Uni uch darsdan keyin, Event loop darsida to'liq ochamiz. Hozircha shu rasm yetarli.

4.3 Asinxron — "parallel" degani emas

Boshida bir xato tasavvur paydo bo'lishi tabiiy: "asinxron kod ikkinchi thread'da, parallel ishlaydi". Yo'q. Sizning kodingiz hamon bitta thread'da, birin-ketin bajariladi. Parallel ishlayotgan narsa — kutish: taymerni sanash, serverdan javob kutish, diskdan fayl o'qish. Bu ishlarni host bajaradi, JavaScript esa faqat boshlab qo'yadi va natijani callback orqali oladi.

Otabek bitta. Oshxonada esa Rustam aka va yordamchilari bir vaqtda bir nechta qozonni kuzatadi. Otabek faqat buyurtmani beradi va tayyorini olib boradi.

Tekshirib ko'ring: setTimeout(fn, 1000) dan keyingi qatorda bandQil(3000) chaqirilsa, fn qachon ishlaydi?

Javob

Taxminan 3000 ms dan keyin. Taymer 1000 ms da tugaydi va fn navbatga tushadi. Lekin stekda hali bandQil turibdi. fn stek bo'shashini — 3000 ms gacha — kutadi. Taymer "kamida 1000 ms" ni kafolatlaydi, "aynan 1000 ms" ni emas.

5. Kutishni kim qiladi: host

5.1 Dvigatel va host

JavaScript kodingizni ikki qism birgalikda ishga tushiradi:

  • JavaScript dvigateli (engine) — kodni o'qib bajaradigan dastur. Chrome va Node'da u V8 deb ataladi. Unda call stack, o'zgaruvchilar, funksiyalar bor. Lekin unda taymer ham, tarmoq ham, ekran ham yo'q.
  • Host muhit (host environment) — dvigatelni o'z ichiga olgan va unga "tashqi dunyo"ni beradigan dastur: brauzer yoki Node. Taymerlar, tugma bosishlari, fayllar, tarmoq — hammasi hostdan keladi.

«Bahor» tilida: dvigatel — Otabek, u faqat buyurtmani olib boradi. Host — butun oshxona: qozonlar, soat, qo'ng'iroq. Otabekda soat yo'q, lekin oshxonada bor.

Host API — host JavaScript'ga beradigan funksiyalar va obyektlar. Brauzerdagilari Web API'lar deb ataladi: setTimeout, document, addEventListener va boshqalar. Ular JavaScript tilining o'zida yo'q, ularni brauzer qo'shadi.

Buni DOM darsidan sezgansiz: document Node'da yo'q edi. Chunki document — brauzerning API'si, til emas. setTimeout esa ikkala hostda ham bor — har biri uni o'zi amalga oshiradi.

Sxemaga qarang: chapda dvigatel, o'ngda host. Strelkalar asinxron ishning yo'lini ko'rsatadi.

flowchart LR
  subgraph D["JavaScript dvigateli (V8)"]
    S["Call stack"]
  end
  subgraph H["Host: brauzer yoki Node"]
    T["Taymerlar"]
    E["Hodisalar"]
    F["Fayl va tarmoq"]
    N["Navbat"]
  end
  S -- "boshlab qo'yadi" --> T
  S -- "tinglovchi qo'shadi" --> E
  S -- "so'rov beradi" --> F
  T -- "vaqt keldi" --> N
  E -- "bosildi" --> N
  F -- "javob keldi" --> N
  N -- "stek bo'shasa" --> S

Stek uch xil ishni hostga topshiradi. Host tayyor bo'lganini navbatga qo'yadi. Navbatdagilar stek bo'shaganda birin-ketin bajariladi.

5.2 Siz allaqachon asinxron kod yozgansiz

09-qismdagi har bir hodisa tinglovchisi — asinxron callback:

js
const tugma = document.querySelector("#bron-tugma");

tugma.addEventListener("click", () => {
  console.log("Bron tugmasi bosildi");
});

console.log("Tinglovchi qo'shildi");

addEventListener callback'ni darhol chaqirmaydi. U brauzerga "bosilganda shuni chaqir" deb topshiradi va qaytadi. Konsolda avval "Tinglovchi qo'shildi" chiqadi. Callback esa foydalanuvchi bosganda — bir soniyadan keyinmi, bir soatdan keyinmi — chaqiriladi. Bosish ham navbatga tushadi va stek bo'shashini kutadi. Execution context darsidagi savolni eslang: tinglovchi 5 soniya ishlasa, ikkinchi bosish kutib turadi. Endi bilasiz — u navbatda turadi.

5.3 Node'da: fayl o'qishning ikki yo'li

Node'da asinxronlikning eng ko'p uchraydigan joyi — fayllar va tarmoq. node:fs modulida (ESM import darsida ishlatgan edik) bir xil ishning ikki varianti bor:

  • readFileSync — sinxron: fayl o'qilguncha kutadi va matnni qaytaradi. Nomi oxiridagi Sync shuni bildiradi.
  • readFile — asinxron: o'qishni Node'ga topshiradi va darhol qaytadi. Matn tayyor bo'lganda callback chaqiriladi.

Ishga tushirishdan oldin o'ylab ko'ring: uchta yozuv qaysi tartibda chiqadi?

js
import { writeFileSync, readFileSync, readFile } from "node:fs";

writeFileSync("menyu.txt", "osh, lag'mon, manti");

const matn = readFileSync("menyu.txt", "utf8");
console.log("Sinxron:", matn);

readFile("menyu.txt", "utf8", (xato, matn) => {
  console.log("Asinxron:", matn);
});

console.log("Kod oxiriga yetdi");

Konsolda:

text
Sinxron: osh, lag'mon, manti
Kod oxiriga yetdi
Asinxron: osh, lag'mon, manti

"Kod oxiriga yetdi" asinxron o'qishdan oldin chiqdi. readFile faylni o'qishni boshlab qo'ydi va qaytdi. Fayl kichik, disk tez — baribir callback faqat joriy kod tugagandan keyin ishladi. Callback'ning birinchi parametri xato — Node'ning maxsus kelishuvi. Uni Callbacklar va callback hell darsida ochamiz.

Bu blok ostida «Ishga tushir» tugmasi yo'q: node:fs faqat Node'da bor, brauzerda emas. Kompyuteringizda sinang: kodni fayl.mjs ga saqlang va terminalda node fayl.mjs deb yozing.

Tekshirib ko'ring: Node serveri bir vaqtda 100 ta foydalanuvchiga xizmat qiladi. Kimdir katta fayl so'radi va server uni readFileSync bilan 2 soniya o'qidi. Qolgan 99 kishi nima his qiladi?

Javob

Ular ham 2 soniya kutadi. Serverning JavaScript'i ham bitta thread'da ishlaydi. Sinxron readFileSync stekni 2 soniya egallaydi — bu vaqt ichida server hech kimga javob bera olmaydi. Asinxron readFile bilan esa o'qishni Node bajaradi, JavaScript esa shu orada boshqa so'rovlarga xizmat qiladi. Shuning uchun serverda asinxron variant tanlanadi.

6. Callback sinxronmi yoki asinxronmi?

Kodga qarab buni qanday bilasiz? Funksiya nomi yoki ko'rinishi aytmaydi — hujjat aytadi. Lekin oddiy qoida bor: callback biror narsa sodir bo'lishini kutsa — vaqt o'tishi, foydalanuvchi harakati, fayl yoki tarmoqdan javob — u asinxron. Faqat qo'ldagi ma'lumotni aylansa — sinxron.

Callback oladi Turi Qachon chaqiriladi
forEach, map, filter, toSorted sinxron metod ishlayotganda, darhol
setTimeout, setInterval asinxron vaqt o'tgach
addEventListener asinxron hodisa bo'lganda
readFile (Node) asinxron fayl o'qilgach

setInterval — setTimeout ning takrorlanuvchi qardoshi. Uni keyingi darsda o'rganamiz.

O'zingiz to'ldiring: [1, 2, 3].find((son) => son > 1) dagi callback chaqiriladi.

Maslahat: Shubha bo'lsa — sinab ko'ring. Callback ichiga va undan keyingi qatorga console.log qo'ying. Callback'dagi yozuv keyin chiqsa — u asinxron.

7. Asinxron natijani return bilan olib bo'lmaydi

Endi eng muhim oqibat. «Bahor»ning stajyor dasturchisi Sardor narxni "oshxonadan so'raydigan" funksiya yozdi. Oshxona javob berishi uchun biroz vaqt kerak — buni setTimeout bilan taqlid qildi:

js
function narxniSora(taom) {
  setTimeout(() => {
    const menyu = { osh: 35000, manti: 30000 };
    return menyu[taom];
  }, 500);
}

const narx = narxniSora("osh");
console.log(narx); // undefined

undefined! Nega?

  • return menyu[taom] — callback'ning return i, narxniSora niki emas. U 500 ms dan keyin, setTimeout ga qaytadi. setTimeout esa uni hech qayerga bermaydi.
  • narxniSora ning o'zida return yo'q. U setTimeout ni chaqirdi va darhol tugadi — undefined qaytardi.
  • console.log(narx) bajarilganda callback hali ishlamagan ham edi.

Asinxron natija hali yo'q paytda funksiya allaqachon qaytib bo'ladi. Shuning uchun natijani return bilan "tashqariga olib chiqib" bo'lmaydi. Uni olishning yo'li — natijani ishlatadigan kodni ham callback ichiga berish:

js
function narxniSora(taom, javobniOl) {
  setTimeout(() => {
    const menyu = { osh: 35000, manti: 30000 };
    javobniOl(menyu[taom]);
  }, 500);
}

narxniSora("osh", (narx) => {
  console.log(`Osh: ${narx} so'm`);
});
console.log("So'rov yuborildi");

Konsolda:

text
So'rov yuborildi
Osh: 35000 so'm

Bu safar narxni ishlatadigan kod — console.log — callback ichida. U narx tayyor bo'lgan paytda chaqiriladi, oldin emas.

Tekshirib ko'ring: Ikkinchi variantda "So'rov yuborildi" nega osh narxidan oldin chiqdi?

Javob

narxniSora taymerni ro'yxatga oldi va darhol qaytdi. Keyingi qator — "So'rov yuborildi" — shu zahoti bajarildi. Narx esa 500 ms dan keyin, taymer callback'i ichida javobniOl chaqirilganda keldi. Asinxron funksiyadan keyingi qator har doim kutmasdan ishlaydi.

Bu yo'l ishlaydi, lekin o'z muammolari bor. Ularni Callbacklar va callback hell darsida ko'ramiz. Keyin JavaScript'ning zamonaviy yechimi — Promise va async/await ni o'rganamiz (Promise asoslari). Ular ham aynan shu muammoni hal qiladi: "natija keyinroq keladi, uni qanday kutamiz?"

8. Ko'p uchraydigan xatolar

8.1 Asinxron natijani darhol ishlatish

js
let menyu = [];

setTimeout(() => {
  menyu = ["osh", "lag'mon", "manti"];
}, 300);

console.log(`Menyuda ${menyu.length} ta taom`); // Menyuda 0 ta taom

console.log callback'dan oldin ishladi — menyu hali bo'sh. Bu xato real loyihalarda juda ko'p uchraydi: ma'lumot serverdan kelishidan oldin ekranga chiziladi. Tuzatish: natijaga bog'liq kodni callback ichiga ko'chiring (yoki callback'dan chaqiring).

8.2 "0 ms — darhol" deb o'ylash

js
setTimeout(() => console.log("taymer"), 0);
console.log("oxirgi qator");

Konsolda:

text
oxirgi qator
taymer

0 ms bo'lsa ham callback joriy kod tugagandan keyin ishlaydi — u navbat orqali o'tadi. Closure tuzoqlari darsidagi var li sikl ham aynan shu sabab bilan 4-urinish ni uch marta chiqargan edi.

8.3 Og'ir ishni tinglovchi ichida qilish

Tugma bosilganda 100 000 ta qatorni saralash yoki katta hisob-kitob — tinglovchi tugaguncha sahifa qotadi. Foydalanuvchi "tugma ishlamayapti" deb yana va yana bosadi — bosishlar navbatda yig'iladi. Tuzatish: avval o'lchang (console.time), og'ir joyni toping. 50 ms dan oshadigan ishni qisqartiring yoki bo'laklarga bo'ling — usullarini Event loop va Asinxron naqshlar darslarida ko'ramiz.

8.4 Bo'sh sikl bilan "kutish"

while (Date.now() < tugash) {} — bu kutish emas, bloklash. Bu darsdagi bandQil faqat bloklashni ko'rsatish uchun yozilgan. Ma'lum vaqtdan keyin biror ish qilish kerak bo'lsa — setTimeout.

9. Mashqlar

1-mashq (oson): Tartibni oldindan ayting

Kodni ishga tushirmasdan, konsolda nima va qaysi tartibda chiqishini yozing. Keyin tekshiring.

js
console.log("A: stol tayyorlandi");

setTimeout(() => {
  console.log("B: manti keldi");
}, 400);

setTimeout(() => {
  console.log("C: choy keldi");
}, 100);

console.log("D: mehmon o'tirdi");
Yechim

Konsolda:

text
A: stol tayyorlandi
D: mehmon o'tirdi
C: choy keldi
B: manti keldi

Avval ikkala sinxron qator — A va D. Taymerlar esa vaqtiga qarab: choy 100 ms da, manti 400 ms da. Kodda manti oldin yozilgani ahamiyatsiz — taymer qachon ro'yxatga olingani emas, qachon tugagani muhim.

2-mashq (o'rta): Sinxronmi yoki asinxronmi?

Har bir blokda "Oxiri" yozuvi callback'dagi yozuvdan oldin chiqadimi yoki keyin? Avval javob bering, keyin sinab ko'ring.

js
// 1
[35000, 28000].forEach((narx) => console.log(narx));
console.log("1-oxiri");

// 2
setTimeout(() => console.log("taymer"), 0);
console.log("2-oxiri");

// 3
const qimmat = [35000, 28000].filter((narx) => {
  console.log("tekshirildi:", narx);
  return narx > 30000;
});
console.log("3-oxiri", qimmat);
Yechim

Konsolda:

text
35000
28000
1-oxiri
tekshirildi: 35000
tekshirildi: 28000
3-oxiri [ 35000 ]
taymer

Birinchi va uchinchi blok — sinxron: forEach va filter callback'lari darhol, metod ichida chaqiriladi. Ikkinchisi — asinxron: "taymer" butun fayl tugagandan keyin, hatto "3-oxiri" dan ham keyin chiqdi. 0 ms bo'lsa ham taymer callback'i joriy kod tugashini kutadi.

3-mashq (qiyin): Bloklashni o'lchang

kurs/mashqlar/11/01-bloklash/olchov.mjs faylini yarating. Uchta taymer qo'ying: 100, 200 va 300 ms. Har biri o'tgan vaqtni 100 ga yaxlitlab chiqarsin (Math.round(ms / 100) * 100). Taymerlardan keyin bandQil(500) chaqiring. Natijani oldindan ayting: taymerlar qaysi tartibda va qachon ishlaydi?

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

const boshi = Date.now();
const yaxlitla = (ms) => Math.round(ms / 100) * 100;

for (const kechikish of [100, 200, 300]) {
  setTimeout(() => {
    const otdi = yaxlitla(Date.now() - boshi);
    console.log(`${kechikish} ms li taymer: ${otdi} ms da`);
  }, kechikish);
}

bandQil(500);
console.log("Band qilish tugadi");

Konsolda:

text
Band qilish tugadi
100 ms li taymer: 500 ms da
200 ms li taymer: 500 ms da
300 ms li taymer: 500 ms da

500 ms davomida uchala taymerning ham vaqti keldi va ular navbatga tushdi. Stek bo'shagach, uchalasi ketma-ket, bir zumda bajarildi — tartibi saqlandi, lekin kechikishlar "yo'qoldi". Bloklash vaqtni "siqib qo'yadi": 100 ms oraliq bilan rejalashtirilgan ishlar bir lahzada bajarildi.

Commit — mashqlar repoda:

bash
git add 11/01-bloklash/olchov.mjs
git commit -m "11/01: bloklashni o'lcha"

4-mashq: Vazifalar qadami — qotishni ko'ring

Bu safar vazifalar kodini o'zgartirmaymiz — bloklashni o'z ilovangizda ko'ramiz. Natijani kurs/mashqlar/11/01-bloklash/javoblar.md ga yozing.

  1. vazifalar ni Live Server'da oching, DevTools → Console.

  2. Quyidagini konsolga yozing, lekin Enter ni bosishdan oldin sichqonchani "Non olish" katagi ustiga olib boring:

    js
    const s = Date.now(); while (Date.now() - s < 3000) {}
  3. Enter bosing va darhol katakni bosing, keyin maydonga "Choy" deb yozing.

  4. Savollar: katak qachon belgilandi? Maydondagi harflar qachon chiqdi? Nima uchun?

Yechim

javoblar.md:

text
Katak 3 soniyadan keyin belgilandi, harflar ham 3 soniyadan
keyin birdaniga chiqdi. Konsoldagi sikl call stack'ni 3 soniya
egalladi. Bosish va tugma bosishlari navbatda kutdi va stek
bo'shagach birin-ketin bajarildi — hech biri yo'qolmadi.

DevTools konsolida yozilgan kod ham sahifaning o'sha bitta thread'ida ishlaydi. Shuning uchun konsoldan ishga tushirilgan og'ir sikl ham butun ilovani qotiradi. Hodisalar yo'qolmagani muhim: brauzer ularni navbatda saqlaydi.

bash
git add 11/01-bloklash/javoblar.md
git commit -m "11/01: vazifalar ilovasida bloklashni kuzat"

10. Real ishda

  • Tez javob beradigan interfeys. Google sayt sifatini INP (Interaction to Next Paint — bosishdan keyingi chizishgacha bo'lgan vaqt) bilan o'lchaydi. Bu vaqt 200 ms gacha bo'lsa — yaxshi. Brauzerda 50 ms dan uzun davom etgan har sinxron ish "uzun task" (long task) hisoblanadi. Buni Performance API va Core Web Vitals darsida o'lchaymiz.
  • Node serverlar. Node bitta thread'da minglab foydalanuvchiga xizmat qiladi — faqat kod bloklamagani uchun. Server kodida readFileSync kabi sinxron funksiyalar faqat ishga tushirishda ishlatiladi, so'rovlarga javob berishda emas. Bu farqni Blocking va non-blocking I/O darsida chuqur ko'ramiz.
  • Og'ir hisob. Rasmni qayta ishlash yoki katta faylni tahlil qilish kabi ishlar uchun brauzerda alohida thread bor — Web Worker. U 13-qismda, Web Workers darsida; hozir bilish shart emas.
  • Intervyu. "JavaScript bir threadli bo'lsa, asinxronlik qanday ishlaydi?", "Sinxron va asinxron kod farqi?" — junior intervyularining doimiy savollari. Javobning kaliti: kutishni host bajaradi, callback navbat orqali stekka qaytadi.

Xulosa

  • Sinxron kod — qator oldingisi tugashini kutadi; yozilish va bajarilish tartibi bir xil.
  • JavaScript bir threadli: call stack bitta, bir vaqtda bitta ish.
  • Bloklash — uzoq sinxron ish stekni egallaydi: taymerlar kechikadi, sahifa qotadi, ekran chizilmaydi.
  • Asinxron kod ishni boshlab qo'yadi va kutmasdan davom etadi; natija callback orqali, keyinroq keladi.
  • Kutishni JavaScript emas, host (brauzer yoki Node) bajaradi; tayyor callback navbatda stek bo'shashini kutadi.
  • Asinxron natijani return bilan olib bo'lmaydi — uni ishlatadigan kod callback ichida bo'lishi kerak.

Keyingi dars: Taymerlar: setTimeout va setInterval — eng oddiy asinxron API'ni chuqur o'rganamiz: taymerni bekor qilish, takrorlash va nega "1000 ms" aslida "kamida 1000 ms" degani.

Manbalar

  • MDN: "Introducing asynchronous JavaScript", "Main thread" (Glossary) — developer.mozilla.org
  • Node.js hujjatlari: "Overview of Blocking vs Non-Blocking" — nodejs.org/learn
  • web.dev: "Interaction to Next Paint (INP)" — web.dev/articles/inp
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Sinxron va asinxron JavaScript: nega kod kutib o'tirmasligi kerak — IlmHamroh