Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sinxron kod: navbat bilan, birin-ketin
- 2.1 Siz yozib kelgan kod
- 2.2 Sinxron callback'lar ham bor
- 3. Bitta thread va bloklash
- 3.1 Bir threadli til
- 3.2 Bloklash
- 3.3 Brauzerda: qotgan sahifa
- 4. Asinxron kod: "keyinroq bajariladi"
- 4.1 Kutishni topshirish
- 4.2 Qadamma-qadam: kutish qayerda bo'ladi?
- 4.3 Asinxron — "parallel" degani emas
- 5. Kutishni kim qiladi: host
- 5.1 Dvigatel va host
- 5.2 Siz allaqachon asinxron kod yozgansiz
- 5.3 Node'da: fayl o'qishning ikki yo'li
- 6. Callback sinxronmi yoki asinxronmi?
- 7. Asinxron natijani return bilan olib bo'lmaydi
- 8. Ko'p uchraydigan xatolar
- 8.1 Asinxron natijani darhol ishlatish
- 8.2 "0 ms — darhol" deb o'ylash
- 8.3 Og'ir ishni tinglovchi ichida qilish
- 8.4 Bo'sh sikl bilan "kutish"
- 9. Mashqlar
- 1-mashq (oson): Tartibni oldindan ayting
- 2-mashq (o'rta): Sinxronmi yoki asinxronmi?
- 3-mashq (qiyin): Bloklashni o'lchang
- 4-mashq: Vazifalar qadami — qotishni ko'ring
- 10. Real ishda
- Xulosa
- Manbalar
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,fnesa 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:
- Buyurtmani oshxonaga beradi va oshxona eshigi oldida 20 daqiqa turadi. Bu orada boshqa stollar chaqiradi, choy so'raydi, hisob kutadi — hech kim javob olmaydi.
- 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:
console.log("Buyurtma qabul qilindi");
const jami = 35000 * 2 + 5000;
console.log(`Hisob: ${jami} so'm`);
console.log("Chek chiqarildi");Konsolda:
Buyurtma qabul qilindi
Hisob: 75000 so'm
Chek chiqarildiIkkinchi 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:
const taomlar = ["osh", "manti"];
console.log("Boshlandi");
taomlar.forEach((taom) => console.log(`- ${taom}`));
console.log("Tugadi");Konsolda:
Boshlandi
- osh
- manti
TugadiforEach 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.
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:
Og'ir ish tugadi
Taymer ishladi: 1000 msTaymerga 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.
<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
bandQildan 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:
console.log("Buyurtma oshxonaga berildi");
setTimeout(() => {
console.log("Osh tayyor — 5-stolga!");
}, 1000);
console.log("Otabek 3-stolga choy olib bordi");Konsolda:
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 qatordabandQil(3000)chaqirilsa,fnqachon 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" --> SStek 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:
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 oxiridagiSyncshuni 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?
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:
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
readFileSyncbilan 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.logqo'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:
function narxniSora(taom) {
setTimeout(() => {
const menyu = { osh: 35000, manti: 30000 };
return menyu[taom];
}, 500);
}
const narx = narxniSora("osh");
console.log(narx); // undefinedundefined! Nega?
return menyu[taom]— callback'ningreturni,narxniSoraniki emas. U 500 ms dan keyin,setTimeoutga qaytadi.setTimeoutesa uni hech qayerga bermaydi.narxniSoraning o'zidareturnyo'q. UsetTimeoutni chaqirdi va darhol tugadi —undefinedqaytardi.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:
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:
So'rov yuborildi
Osh: 35000 so'mBu 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
let menyu = [];
setTimeout(() => {
menyu = ["osh", "lag'mon", "manti"];
}, 300);
console.log(`Menyuda ${menyu.length} ta taom`); // Menyuda 0 ta taomconsole.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
setTimeout(() => console.log("taymer"), 0);
console.log("oxirgi qator");Konsolda:
oxirgi qator
taymer0 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.
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:
A: stol tayyorlandi
D: mehmon o'tirdi
C: choy keldi
B: manti keldiAvval 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.
// 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:
35000
28000
1-oxiri
tekshirildi: 35000
tekshirildi: 28000
3-oxiri [ 35000 ]
taymerBirinchi 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
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:
Band qilish tugadi
100 ms li taymer: 500 ms da
200 ms li taymer: 500 ms da
300 ms li taymer: 500 ms da500 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:
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.
vazifalarni Live Server'da oching, DevTools → Console.Quyidagini konsolga yozing, lekin Enter ni bosishdan oldin sichqonchani "Non olish" katagi ustiga olib boring:
jsconst s = Date.now(); while (Date.now() - s < 3000) {}Enter bosing va darhol katakni bosing, keyin maydonga "Choy" deb yozing.
Savollar: katak qachon belgilandi? Maydondagi harflar qachon chiqdi? Nima uchun?
Yechim
javoblar.md:
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.
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
readFileSynckabi 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
returnbilan 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
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!