IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq5/32-dars21 daqiqa
Mundarija (35)

JavaScript Promise asoslari: kutilmoqda, bajarildi, rad etildi

Qisqacha: Promise — kelajakda tayyor bo'ladigan natija uchun obyekt. U uch holatdan birida bo'ladi: kutilmoqda (pending), bajarildi (fulfilled) yoki rad etildi (rejected). Yaratish: new Promise((resolve, reject) => ...), natijani olish: chek.then(...), xatoni olish: chek.catch(...). Promise faqat bir marta hal bo'ladi va then callback'lari doim asinxron — microtask navbati orqali — chaqiriladi.

Bu darsda

  • Promise'ning uch holatini va "hal bo'ldi" (settled) nima ekanini tushuntira olasiz.
  • new Promise bilan o'z asinxron funksiyangizni Promise qaytaradigan qilib yoza olasiz.
  • Natijani then, xatoni catch bilan olasiz, finally ni qachon ishlatishni bilasiz.
  • Promise callback'lar muammolarini — ikki marta chaqirish, "goh sinxron, goh asinxron" — qanday hal qilishini ko'rasiz.
  • then callback'i qachon bajarilishini event loop navbatlari bo'yicha oldindan ayta olasiz.

Oldin bilishingiz kerak: Callbacklar va callback hell, Event loop, throw va Error obyekti.

1. Nega bu kerak?

Callbacklar va callback hell darsida callback'larning uchta og'riqli joyini ko'rdik:

  • Bog'liq ishlar ichma-ich piramidaga aylanadi.
  • Callback'ni begona kodga berasiz va u uni ikki marta chaqirishi mumkin — Dilshod aka bitta osh uchun ikki marta "to'ladi".
  • Callback goh sinxron, goh asinxron chaqiriladi va kod tartibi buziladi ("Zalgo").

Hammasining ildizi bitta: asinxron funksiya natijani qaytarmaydi, balki sizning funksiyangizni chaqiradi. Boshqaruv uning qo'lida.

Endi «Bahor»ning yangi kassasini tasavvur qiling. Dilshod aka osh buyurtma qiladi. Kassir unga darhol chek beradi: "Buyurtmangiz raqami — 47. Tayyor bo'lganda ekranda chiqadi". Osh hali yo'q, lekin chek hozir Dilshod akaning qo'lida. U chekni cho'ntagiga solib, choy ichishga ketishi mumkin.

Chekning uch holati bor:

  • tayyorlanmoqda — osh hali qozonda;
  • tayyor — ekranda "47" yondi, oshni olish mumkin;
  • bekor qilindi — osh tugab qoldi, kassir sababini aytadi.

Va chekning ikki muhim xususiyati bor. Birinchisi, "tayyor" bo'lgan chek qaytib "tayyorlanmoqda" bo'lmaydi va "bekor qilindi" ga o'tmaydi. Ikkinchisi, chek Dilshod akaning qo'lida. Oshxona uni chaqirmaydi — u o'zi xohlagan paytda chekka qaraydi.

JavaScript'da shu chek — Promise. Bugungi dars shu haqida.

2. Promise nima?

2.1 Ta'rif va uch holat

Promise — asinxron ishning kelajakdagi natijasini ifodalaydigan obyekt. Inglizcha so'zma-so'z "va'da" degani, lekin kursda atama inglizchaligicha qoladi: dasturchilar shunday aytadi.

Promise har doim uch holatdan birida bo'ladi:

Holat Inglizcha Ma'nosi
kutilmoqda pending ish hali tugamagan, natija yo'q
bajarildi fulfilled ish muvaffaqiyatli tugadi, qiymat bor
rad etildi rejected ish xato bilan tugadi, sabab (odatda Error) bor

Promise "bajarildi" yoki "rad etildi" holatiga o'tsa, u hal bo'ldi (settled) deyiladi. Hal bo'lgan Promise boshqa hech qachon o'zgarmaydi.

stateDiagram-v2
  direction LR
  state "kutilmoqda (pending)" as K
  state "bajarildi (fulfilled)" as B
  state "rad etildi (rejected)" as R
  [*] --> K
  K --> B: resolve(qiymat)
  K --> R: reject(xato)

Diagrammaga qarang: strelkalar faqat "kutilmoqda" dan chiqadi. "Bajarildi" va "rad etildi" — oxirgi bekatlar, ulardan yo'l yo'q.

2.2 Promise'ni ko'rish

Promise — oddiy obyekt. Uni o'zgaruvchiga saqlash, funksiyaga berish, console.log bilan chiqarish mumkin. Yaratishni keyingi bo'limda batafsil ko'ramiz, hozir faqat holatlarga qarang:

js
const chek = new Promise((resolve) => {
  setTimeout(() => resolve(35000), 100);
});

console.log(chek);
setTimeout(() => console.log(chek), 200);

Konsolda:

text
Promise { <pending> }
Promise { 35000 }

Birinchi console.log darhol ishladi — taymer hali tugamagan, Promise kutilmoqda. 200 ms dan keyin esa u bajarildi, ichida qiymat — 35000. Node bajarilgan Promise'ni ichidagi qiymat bilan ko'rsatadi.

Brauzer konsoli boshqacha yozadi: Promise {<pending>}, ochsangiz esa [[PromiseState]]: "fulfilled" va [[PromiseResult]]: 35000. Ma'nosi bir xil.

Tekshirib ko'ring: Promise "bajarildi" holatidan "rad etildi" holatiga o'ta oladimi?

Javob

Yo'q. "Bajarildi" va "rad etildi" — ikkalasi ham hal bo'lgan holat, Promise ulardan chiqmaydi. Faqat "kutilmoqda" dan bir marta, ikki yo'lning biriga o'tiladi.

3. Promise yaratish: new Promise

3.1 Executor, resolve va reject

Promise'ni new Promise(...) bilan yaratamiz. Unga bitta funksiya beriladi — u executor deyiladi: asinxron ishni boshlab beradigan funksiya. Atama inglizchaligicha qoladi, dasturchilar shunday aytadi. «Bahor» tilida executor — kassir chekni bergandan keyin oshxonada boshlangan ish: qozon yonadi, osh pishadi.

Executor ikki parametr oladi:

  • resolve(qiymat) — "ish bajarildi, natija mana bu". Promise "bajarildi" holatiga o'tadi.
  • reject(xato) — "ish bajarilmadi, sababi mana bu". Promise "rad etildi" holatiga o'tadi.

Chek misolida resolve — ekranda "47" raqamining yonishi, reject — "osh tugab qoldi" degan xabar. Ikkalasini ham Promise'ning o'zi tayyorlab, executor'ga beradi. Siz faqat ish tugaganda ulardan birini chaqirasiz. Nomlarini o'zingiz tanlashingiz mumkin, lekin resolve va reject — hamma joyda qabul qilingan nom. Biz ham shunday yozamiz.

Callbacklar darsidagi narxniSora ni Promise qaytaradigan qilib qayta yozamiz:

js
const menyu = { osh: 35000, manti: 30000 };

function narxniSora(taom) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!Object.hasOwn(menyu, taom)) {
        reject(new Error(`Menyuda yo'q: ${taom}`));
        return;
      }
      resolve(menyu[taom]);
    }, 200);
  });
}

const chek = narxniSora("manti");
console.log(chek);

Konsolda:

text
Promise { <pending> }

Farqqa qarang. Eski narxniSora(taom, callback) hech narsa qaytarmasdi. Yangisi esa callback olmaydi va darhol obyekt qaytaradi — chek. Natija hali yo'q, lekin chek bizning qo'limizda.

Ichida esa tanish kod: o'sha setTimeout, o'sha tekshiruv. Faqat callback(new Error(...)) o'rniga reject(...), callback(null, narx) o'rniga resolve(narx). Error-first kelishuvining ikki yo'li endi ikki alohida funksiya.

3.2 Executor darhol, sinxron ishlaydi

Bir nozik joy. Executor keyinroq emas, new Promise chaqirilgan zahoti, sinxron bajariladi:

js
console.log("1");
const chek = new Promise((resolve) => {
  console.log("2: executor ichida");
  resolve("osh");
  console.log("3: resolve'dan keyin ham davom etadi");
});
console.log("4");

Konsolda:

text
1
2: executor ichida
3: resolve'dan keyin ham davom etadi
4

Ikki xulosa:

  • Executor ichidagi kod — oddiy sinxron kod. Asinxronlik u yerda faqat siz qo'ygan setTimeout (yoki boshqa asinxron ish) tufayli paydo bo'ladi.
  • resolve chaqirilishi funksiyadan chiqmaydi — u faqat Promise holatini o'zgartiradi. Shuning uchun narxniSora da reject dan keyin return qo'ydik: keyingi qatorlar bekorga bajarilmasin.

3.3 Executor ichidagi throw

Executor ichida xato tashlansa, Promise o'zi rad etiladi — dastur to'xtamaydi:

js
const chek = new Promise(() => {
  throw new Error("Gaz o'chib qoldi");
});

chek.catch((xato) => {
  console.log("Ushlandi:", xato.message);
});

Konsolda:

text
Ushlandi: Gaz o'chib qoldi

Bu reject(new Error("Gaz o'chib qoldi")) bilan bir xil. Faqat bitta shart bor: throw executor'ning sinxron qismida bo'lishi kerak. Executor ichidagi setTimeout callback'ida throw qilsangiz — u allaqachon boshqa stekda, Promise uni ko'rmaydi va dastur to'xtaydi (Callbacklar darsidagi "try tugadi" holati). Asinxron joyda doim reject ni chaqiring.

catch ni hozir ko'rdingiz — uni batafsil keyingi bo'limda ko'ramiz.

3.4 Promise ishni boshqarmaydi — u ishni kuzatadi

Executor darhol ishlashidan muhim xulosa chiqadi: ish new Promise paytida boshlanadi. Hech kim then ulamasa ham, taymer baribir ishlaydi:

js
function oshDamla() {
  return new Promise((resolve) => {
    console.log("Rustam aka qozonni yoqdi");
    setTimeout(() => resolve("osh"), 100);
  });
}

const chek = oshDamla();
console.log("Hali hech kim natijani so'ramadi");

Konsolda:

text
Rustam aka qozonni yoqdi
Hali hech kim natijani so'ramadi

then yo'q, lekin qozon yondi. Promise — ishni boshqaradigan "pult" emas, ishning natijasi uchun chek, xolos. Bundan ikki amaliy qoida kelib chiqadi:

  • Ishni keyinroq boshlamoqchi bo'lsangiz, Promise'ni emas, Promise qaytaradigan funksiyani saqlang: oshDamla — hali ish emas, oshDamla() — ish boshlandi.
  • Promise'ning o'zida "to'xtat" tugmasi yo'q. Keraksiz bo'lib qolgan ishni bekor qilish uchun alohida vosita bor — AbortController, shu qismda o'rganamiz.

Birinchi qoida keyingi darslarda juda muhim bo'ladi. Ketma-ket va parallel bajarish darsida aynan "ish qachon boshlanadi?" savoli kodni uch barobar tezlashtiradi.

Tekshirib ko'ring: const a = oshDamla(); const b = oshDamla(); — nechta qozon yonadi?

Javob

Ikkita. Har oshDamla() chaqiruvi yangi Promise yaratadi, har birining executor'i darhol ishlaydi va o'z taymerini qo'yadi. Ikkala ish bir vaqtda ketadi.

Tekshirib ko'ring: Executor return 35000 qilsa, Promise "bajarildi" holatiga o'tadimi?

Javob

Yo'q. Executor'ning qaytargan qiymati e'tiborsiz qoladi. Promise faqat resolve yoki reject chaqirilganda (yoki executor throw qilganda) hal bo'ladi. new Promise(() => { return 35000; }) abadiy "kutilmoqda" holatida qoladi.

4. Natijani olish: then, catch, finally

4.1 then — bajarilganda

Chek qo'limizda. Natija kelganda nima qilishni aytish uchun then metodi bor. Unga funksiya berasiz, Promise bajarilganda u qiymat bilan chaqiriladi:

js
function narxniSora(taom) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(taom === "osh" ? 35000 : 30000), 200);
  });
}

console.log("Oshxonadan so'raldi");
narxniSora("osh").then((narx) => {
  console.log(`Osh: ${narx} so'm`);
});
console.log("Kutish davomida choy quyildi");

Konsolda:

text
Oshxonadan so'raldi
Kutish davomida choy quyildi
Osh: 35000 so'm

Tashqaridan bu callback'ga o'xshaydi — u ham keyinroq chaqiriladi. Lekin farq katta. Callback'ni biz oshxonaga berardik. then ni esa o'z qo'limizdagi chekka ulaymiz. Oshxona bizning funksiyamizni umuman ko'rmaydi: u faqat resolve ni chaqiradi, xolos. Boshqaruv qaytib bizga keldi.

4.2 catch — rad etilganda

catch — Promise rad etilganda chaqiriladigan funksiyani ulaydi. Unga xato (rad etish sababi) beriladi:

js
const menyu = { osh: 35000, manti: 30000 };

function narxniSora(taom) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!Object.hasOwn(menyu, taom)) {
        reject(new Error(`Menyuda yo'q: ${taom}`));
        return;
      }
      resolve(menyu[taom]);
    }, 200);
  });
}

narxniSora("palov")
  .then((narx) => console.log(`Narx: ${narx} so'm`))
  .catch((xato) => console.log("Xato:", xato.message));

Konsolda:

text
Xato: Menyuda yo'q: palov

Promise rad etildi — shuning uchun then dagi funksiya chaqirilmadi, catch dagisi chaqirildi. "palov" o'rniga "osh" yozsangiz — aksincha: Narx: 35000 so'm, catch jim.

Qatorlarning joylashuviga e'tibor bering: .then(...) va .catch(...) yangi qatorda, nuqta bilan boshlanadi. Bu bitta ifoda — narxniSora("palov").then(...).catch(...) — faqat o'qish uchun bo'lingan. Nega then dan keyin catch ni ulash mumkinligini Promise zanjiri darsida ko'ramiz.

then ning ikkinchi argumenti ham bor: chek.then(narxBilan, xatoBilan). U ham rad etishni ushlaydi. Lekin zamonaviy kodda catch ko'proq ishlatiladi — o'qish osonroq. Ikkalasining nozik farqini ham keyingi darsda ko'ramiz.

4.3 finally — har qanday holatda

finally — Promise qanday hal bo'lishidan qat'i nazar chaqiriladi. try / catch / finally dagi finally ning aynan o'zi. Odatda "tozalash" uchun: yuklanish belgisini yashirish, tugmani qayta yoqish.

js
function narxniSora(taom) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (taom === "osh") resolve(35000);
      else reject(new Error(`Menyuda yo'q: ${taom}`));
    }, 100);
  });
}

console.log("Yuklanmoqda...");
narxniSora("palov")
  .then((narx) => console.log(`Narx: ${narx} so'm`))
  .catch((xato) => console.log("Xato:", xato.message))
  .finally(() => console.log("Yuklanish belgisi yashirildi"));

Konsolda:

text
Yuklanmoqda...
Xato: Menyuda yo'q: palov
Yuklanish belgisi yashirildi

finally dagi funksiya argument olmaydi: u natijani ham, xatoni ham bilmaydi — bilishi shart ham emas. "osh" bilan sinasangiz ham, oxirgi qator baribir chiqadi.

Tekshirib ko'ring: Promise rad etildi, lekin sizda faqat then bor, catch yo'q. then dagi funksiya chaqiriladimi?

Javob

Yo'q. then ning (birinchi) funksiyasi faqat bajarilganda chaqiriladi. Rad etilgan Promise'da u jim o'tkazib yuboriladi. Xato esa hech kim ushlamagani uchun «ushlanmagan rad etilish» bo'ladi — buni «Ko'p uchraydigan xatolar» da ko'ramiz.

5. then qachon chaqiriladi: microtask

5.1 Qadamma-qadam

Event loop darsida "microtask'ning asosiy manbai — Promise" degan edik. Endi buni ko'ramiz. Qoida bitta: Promise hal bo'lganda uning then callback'lari microtask navbatiga qo'yiladi.

js
console.log("1. Buyurtma berildi");
const chek = new Promise((resolve) => {
  setTimeout(() => resolve("osh"), 300);
});
chek.then((taom) => {
  console.log(`3. Tayyor: ${taom}`);
});
console.log("2. Chek qo'lda");

Konsolda:

text
1. Buyurtma berildi
2. Chek qo'lda
3. Tayyor: osh

Qadamma-qadam kuzating. O'ngda — chek ning holati va uchta navbat-joy. then callback'i qaysi navbatdan o'tishiga e'tibor bering:

Diqqat qiling: then callback'ni hech qayerga "jo'natmaydi". Chek kutilayotganda callback chekning ichida, ro'yxatda turadi. Faqat resolve chaqirilgan lahzada u microtask navbatiga o'tadi.

5.2 Allaqachon hal bo'lgan Promise

Agar then ni Promise hal bo'lgandan keyin ulasak-chi? Bu yerda executor resolve ni darhol, sinxron chaqiradi. Ko'pchilik "chek tayyor — demak then ham darhol ishlaydi" deb o'ylaydi va 1, 3, 2 ni kutadi. Sinab ko'ramiz:

js
const chek = new Promise((resolve) => resolve("osh"));

console.log("1");
chek.then((taom) => console.log("3:", taom));
console.log("2");

Konsolda:

text
1
2
3: osh

Chek allaqachon bajarilgan bo'lsa ham, then callback'i sinxron kod tugagandan keyin chiqdi. Bu tasodif emas — standartning qat'iy qoidasi: then callback'i hech qachon sinxron chaqirilmaydi. U doim microtask navbatidan o'tadi.

Diqqat: Bu qoida yodda tursin: Promise'ning holati qanday bo'lishidan qat'i nazar, then, catch va finally callback'lari joriy sinxron kod tugagach ishlaydi.

5.3 Kech qo'shilgan then ham natijani oladi

Hodisa tinglovchisi bilan solishtiring. Tugma bosilgandan keyin addEventListener qilsangiz, o'sha bosishni bilmaysiz — u o'tib ketgan. Promise esa natijani eslab qoladi. Bitta chekka istalgancha then ulash mumkin, istalgan paytda:

js
const chek = new Promise((resolve) => {
  setTimeout(() => resolve("osh"), 100);
});

chek.then((taom) => console.log("Dilshod aka:", taom));
setTimeout(() => {
  chek.then((taom) => console.log("Ofitsiant Otabek:", taom));
}, 300);

Konsolda:

text
Dilshod aka: osh
Ofitsiant Otabek: osh

Otabek chekka osh tayyor bo'lgandan 200 ms keyin qaradi — natija hamon o'sha yerda. Ikkalasi ham bitta natijani oldi, oshxona esa oshni bir marta tayyorladi.

Tekshirib ko'ring: Quyidagi kod nima chiqaradi va qaysi tartibda? const c = new Promise((r) => r(1)); c.then(() => console.log("A")); console.log("B");

Javob

Avval B, keyin A. Promise darhol bajarildi, then uning callback'ini microtask navbatiga qo'ydi. Microtask esa sinxron kod (console.log("B")) tugagandan keyin ishlaydi.

6. Promise callback muammolarini qanday hal qiladi

6.1 Faqat bir marta

Callbacklar darsidagi to'lov kutubxonasi callback'ni ikki marta chaqirgan edi. Uni himoya qilish uchun birMarta o'ramini yozdik. Promise'da bu himoya ichida tayyor: birinchi resolve yoki reject dan keyingi hamma chaqiruvlar e'tiborsiz qoladi.

js
// Begona kutubxona — ichida hamon xato bor
function tolovQil(summa) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("to'landi"), 100);
    setTimeout(() => resolve("to'landi"), 150);
    setTimeout(() => reject(new Error("bekor")), 200);
  });
}

let tushum = 0;
tolovQil(35000).then((holat) => {
  tushum += 35000;
  console.log(`To'lov: ${holat}, tushum: ${tushum} so'm`);
});

Konsolda:

text
To'lov: to'landi, tushum: 35000 so'm

Kutubxona resolve ni ikki marta, keyin yana reject ni chaqirdi. Lekin Promise birinchi resolve da hal bo'ldi va "qotib" qoldi. Bizning then aynan bir marta ishladi — birMarta siz.

6.2 Doim asinxron

"Zalgo" muammosi — kesh bo'lsa callback sinxron, bo'lmasa asinxron chaqirilardi. Promise bilan bu ham o'z-o'zidan yo'qoladi. Keshdan olingan narx ham darhol bajarilgan Promise sifatida qaytadi, then esa — biz endigina ko'rdik — doim microtask'da ishlaydi:

js
const kesh = new Map();

function narxniOl(taom) {
  if (kesh.has(taom)) {
    return new Promise((resolve) => resolve(kesh.get(taom)));
  }
  return new Promise((resolve) => {
    setTimeout(() => {
      kesh.set(taom, 35000);
      resolve(35000);
    }, 100);
  });
}

function sotibOl(nomi) {
  console.log(`${nomi}: so'rov`);
  narxniOl("osh").then((n) => console.log(`${nomi}: narx ${n}`));
  console.log(`${nomi}: so'rovdan keyingi qator`);
}

sotibOl("1-mehmon");
setTimeout(sotibOl, 300, "2-mehmon");

Konsolda:

text
1-mehmon: so'rov
1-mehmon: so'rovdan keyingi qator
1-mehmon: narx 35000
2-mehmon: so'rov
2-mehmon: so'rovdan keyingi qator
2-mehmon: narx 35000

Ikkala mehmonda ham tartib bir xil. Callback'li versiyada ikkinchi mehmonda "narx" "keyingi qator" dan oldin chiqqan edi.

6.3 Xato — alohida yo'ldan

Error-first callback'da har safar if (xato) ni yozish kerak edi, unutsangiz — xato yutilardi. Promise'da xato alohida kanaldan keladi: catch ga. then dagi funksiya xatoni umuman ko'rmaydi, uni tekshirish shart emas.

Natijada uchta kafolat, va ularni tilning o'zi beradi:

Callback muammosi Promise'da
ikki marta chaqirish faqat birinchi resolve/reject hisobga olinadi
goh sinxron, goh asinxron then doim microtask'da
xatoni tekshirishni unutish xato alohida — catch ga keladi

Callback hell piramidasi-chi? Uni Promise zanjiri yechadi — keyingi darsning mavzusi.

Tekshirib ko'ring: Begona kutubxona Promise ichida avval reject(new Error("bekor")), keyin resolve("to'landi") ni chaqirdi. Sizning then ishlaydimi yoki catch?

Javob

catch. Promise birinchi chaqiruvda — reject da — rad etildi va hal bo'ldi. Keyingi resolve e'tiborsiz qoladi: hal bo'lgan Promise o'zgarmaydi.

7. Ko'p uchraydigan xatolar

7.1 Ushlanmagan rad etilish

catch siz qolgan rad etilgan Promise — ushlanmagan rad etilish (unhandled rejection):

js
function narxniSora(taom) {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error(`Menyuda yo'q: ${taom}`)), 100);
  });
}

narxniSora("palov").then((narx) => console.log(narx));

Node 24 da (fayl yo'li qisqartirildi):

text
file:///C:/.../11/rad.mjs:3
    setTimeout(() => reject(new Error(`Menyuda yo'q: ${taom}`)), 100);
                            ^

Error: Menyuda yo'q: palov
    at Timeout._onTimeout (file:///C:/.../11/rad.mjs:3:29)
    at listOnTimeout (node:internal/timers:685:17)
    at process.processTimers (node:internal/timers:618:7)

Node.js v24.21.0

Node 15 dan beri ushlanmagan rad etilish dasturni to'xtatadi — xuddi ushlanmagan throw kabi. Brauzer sahifani to'xtatmaydi, lekin konsolga qizil xabar chiqaradi: Uncaught (in promise) Error: Menyuda yo'q: palov — "ushlanmagan (Promise ichida) xato". Ikkala holatda ham xato e'tibordan chetda qoldi degani.

Agar reject ga Error emas, oddiy satr berilsa, Node boshqacha matn chiqaradi:

text
UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "palov yo'q".

Tarjimasi: "Bu xato yo async funksiya ichida catch blokisiz throw qilishdan, yo .catch() bilan ushlanmagan Promise'ni rad etishdan kelib chiqdi. Promise «palov yo'q» sababi bilan rad etildi". Async funksiyalarni async / await darsida ko'ramiz. Xabarda stack trace ham yo'q — satrda u bo'lmaydi. Shuning uchun doim Error bilan rad eting.

Tuzatish: har Promise zanjirining oxirida catch bo'lsin. Global "oxirgi himoya chizig'i" ham bor — Ushlanmagan xatolar darsida.

7.2 resolve ni hech qachon chaqirmaslik

js
function stolTop(odam) {
  return new Promise((resolve) => {
    if (odam <= 20) {
      setTimeout(() => resolve(5), 100);
    }
    // odam > 20 bo'lsa — hech narsa chaqirilmaydi!
  });
}

stolTop(25) abadiy "kutilmoqda" bo'lib qoladi: na then, na catch, na finally ishlaydi. Xato xabari ham yo'q — eng sezilmaydigan xato. Tuzatish: executor'dagi har yo'l resolve yoki reject bilan tugasin. if bo'lsa — else da reject.

7.3 then dan natijani tashqariga olib chiqish

js
let narx;
narxniSora("osh").then((n) => {
  narx = n;
});
console.log(narx); // undefined

Callback'lardagi eski xato, yangi kiyimda. then callback'i sinxron koddan keyin ishlaydi — console.log paytida narx hali bo'sh. Tuzatish: natijaga bog'liq kod then ichida bo'lsin yoki uni keyingi then ga uzating (Promise zanjiri).

7.4 then ga funksiya o'rniga chaqiruv berish

chek.then(console.log("tayyor")) — console.log darhol chaqiriladi va undefined qaytaradi. then ga esa undefined beriladi va u jim e'tiborsiz qoldiriladi. Taymerlar darsidagi setTimeout(salomBer(), 1000) xatosining o'zi. Tuzatish: chek.then(() => console.log("tayyor")).

7.5 "Promise'ga o'rasam — og'ir ish asinxron bo'ladi"

Sardor oy oxiridagi og'ir hisobni sahifa qotmasin deb Promise'ga o'radi:

js
function hisobla() {
  return new Promise((resolve) => {
    let summa = 0;
    for (let i = 0; i < 3_000_000_000; i++) summa += i % 100;
    resolve(summa);
  });
}

Sahifa baribir qotadi. Sababini «Executor darhol, sinxron ishlaydi» bo'limida ko'rdik: sikl new Promise paytida, stekda bajariladi. Promise kutishni o'zi yaratmaydi — u faqat host bajaradigan kutishning (taymer, fayl, tarmoq) natijasini yetkazadi. Tuzatish: og'ir hisobni bo'laklarga bo'lish (Event loop darsidagi 3-mashq) yoki alohida thread'ga chiqarish (Web Workers, 13-qismda).

8. Mashqlar

1-mashq (oson): Bron Promise bilan

Callbacklar darsidagi stolniBronQil ni Promise qaytaradigan qilib qayta yozing. stolniBronQil(odam) 200 ms dan keyin: odam 1 dan 20 gacha bo'lsa — "<odam> kishilik stol bron qilindi" bilan bajarilsin, aks holda Error("Odam soni 1–20 oralig'ida bo'lsin") bilan rad etilsin. 3 va 0 bilan then/catch orqali sinang.

Yechim
js
function stolniBronQil(odam) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (odam < 1 || odam > 20) {
        reject(new Error("Odam soni 1–20 oralig'ida bo'lsin"));
        return;
      }
      resolve(`${odam} kishilik stol bron qilindi`);
    }, 200);
  });
}

stolniBronQil(3)
  .then((xabar) => console.log(xabar))
  .catch((xato) => console.log("Xato:", xato.message));

stolniBronQil(0)
  .then((xabar) => console.log(xabar))
  .catch((xato) => console.log("Xato:", xato.message));

Konsolda:

text
3 kishilik stol bron qilindi
Xato: Odam soni 1–20 oralig'ida bo'lsin

Callback'li versiyada natijaniYoz(xato, xabar) ichida if (xato) kerak edi. Bu yerda ikki yo'l ikki alohida funksiyada: then faqat muvaffaqiyatni, catch faqat xatoni ko'radi.

2-mashq (o'rta): Tartibni oldindan ayting

Avval qog'ozda har qatorga "S" (sinxron), "M" (microtask) yoki "T" (taymer task'i) deb belgi qo'ying, keyin chiqish tartibini yozing. Ishora: executor — sinxron; hal bo'lgan Promise'ning then callback'i — microtask.

js
console.log("A");
setTimeout(() => console.log("B"), 0);
const chek = new Promise((resolve) => {
  console.log("C");
  resolve();
});
chek.then(() => console.log("D"));
queueMicrotask(() => console.log("E"));
console.log("F");
Yechim

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

Konsolda:

text
A
C
F
D
E
B

Sinxronlar kod tartibida: A, C (executor darhol ishlaydi), F. Keyin microtask navbati: D birinchi qo'yilgan (chek allaqachon bajarilgan edi, then callback'ni darhol navbatga qo'ydi), keyin E. Oxirida taymer task'i — B.

3-mashq (qiyin): Callback'li funksiyani Promise'ga o'rash

Node'ning readFile funksiyasi callback'li (Callbacklar darsida ko'rgansiz). kurs/mashqlar/11/05-promise/faylniOqi.mjs faylida faylniOqi(yol) funksiyasini yozing. U readFile(yol, "utf8", callback) ni chaqirsin va Promise qaytarsin: xato bo'lsa — rad etilsin (xatoning o'zi bilan), bo'lmasa — fayl matni bilan bajarilsin. Sinash: mavjud bo'lmagan "bronlar.json" ni o'qib, catch da xato.code ni chiqaring.

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

function faylniOqi(yol) {
  return new Promise((resolve, reject) => {
    readFile(yol, "utf8", (xato, matn) => {
      if (xato) {
        reject(xato);
        return;
      }
      resolve(matn);
    });
  });
}

faylniOqi("bronlar.json")
  .then((matn) => console.log("Fayl uzunligi:", matn.length))
  .catch((xato) => console.log("O'qib bo'lmadi:", xato.code))
  .finally(() => console.log("Tugadi"));

Konsolda:

text
O'qib bo'lmadi: ENOENT
Tugadi

Bu — callback'li API'ni Promise'ga aylantirishning umumiy naqshi: executor ichida eski funksiyani chaqirasiz, error-first callback'ning ikki yo'lini reject va resolve ga ulaysiz. reject(xato) ga Node'ning o'z Error obyektini beryapmiz — unda code bor. Bu naqsh shunchalik ko'p uchraydiki, Node'da uning tayyor vositasi bor — Promise yordamchilari va promisify darsida.

Mavjud faylni ham sinang: faylniOqi(import.meta.filename) — fayl o'zini o'qiydi.

bash
git add 11/05-promise/faylniOqi.mjs
git commit -m "11/05: readFile ni Promise'ga o'rash"

9. Real ishda

  • Har joyda Promise. Zamonaviy JavaScript'da kutish kerak bo'lgan deyarli hamma API Promise qaytaradi. Brauzerda: tarmoqdan ma'lumot olish (fetch asoslari — 11-qismning ikkinchi yarmida, hozir bilish shart emas), clipboard, kamera, audio.play(). Node'da: node:fs/promises, ma'lumotlar bazasi drayverlari.
  • Kutubxonalar. To'lov tizimlari (Click, Payme SDK'lari), Telegram bot kutubxonalari, ORM'lar — hammasi Promise bilan ishlaydi. Callback'li eski API'lar ham Promise'ga o'raladi — 3-mashqdagidek.
  • Sintaksis ustiga sintaksis. Kundalik kodda then o'rniga ko'pincha async/await yoziladi. Lekin u Promise'ning ustiga qurilgan: Promise'ni tushunmasdan await xatolarini topib bo'lmaydi.
  • Intervyu. "Promise'ning holatlari qanday?", "Executor qachon ishlaydi?", "then callback'i sinxronmi?", "Bu kod qaysi tartibda chiqaradi?" — juda ko'p so'raladi.

Xulosa

  • Promise — kelajakdagi natija uchun obyekt: kutilmoqda → bajarildi yoki rad etildi. Hal bo'lgach o'zgarmaydi.
  • new Promise((resolve, reject) => ...) — executor darhol, sinxron ishlaydi; ish tugaganda resolve(qiymat) yoki reject(xato) chaqiriladi.
  • then — bajarilganda, catch — rad etilganda, finally — har qanday holatda (argumentsiz).
  • then callback'lari doim microtask navbati orqali, sinxron koddan keyin ishlaydi — Promise allaqachon hal bo'lgan bo'lsa ham.
  • Promise kafolatlari: faqat bir marta, doim asinxron, xato alohida kanalda.
  • Rad etilgan Promise'ni doim catch qiling va doim Error bilan rad eting.

Keyingi dars: Promise zanjiri — then doim yangi Promise qaytaradi: callback hell piramidasi tekis zanjirga aylanadi, xato esa zanjir bo'ylab pastga, bitta catch gacha oqadi.

Manbalar

  • ECMAScript spetsifikatsiyasi: "Promise Objects" — tc39.es/ecma262
  • MDN: "Promise", "Using promises" — developer.mozilla.org
  • Node.js hujjatlari: "--unhandled-rejections" (throw rejimi, Node 15 dan standart) — nodejs.org
  • Jake Archibald, "JavaScript Promises: an introduction" — web.dev
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Promise asoslari: kutilmoqda, bajarildi, rad etildi — IlmHamroh