Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. then yangi Promise qaytaradi
- 2.1 Ikkita har xil Promise
- 2.2 Qiymat qaytarish: konveyer
- 2.3 Promise qaytarish: zanjir kutadi
- 2.4 Qoida bitta jadvalda
- 3. Callback hell → tekis zanjir
- 3.1 To'rt qadam — to'rt qator
- 3.2 Yanada qisqa
- 3.3 Zanjirni funksiyadan qaytaring
- 4. Bajarilish tartibi: har then — alohida microtask
- 5. Xato zanjir bo'ylab oqadi
- 5.1 Rad etilgan Promise then larni o'tkazib yuboradi
- 5.2 then ichidagi throw
- 5.3 catch dan keyin zanjir davom etadi
- 5.4 then(ok, xato) va then(ok).catch(xato) farqi
- 6. finally zanjirda
- 7. Ko'p uchraydigan xatolar
- 7.1 return ni unutish
- 7.2 Ichma-ich then (Promise piramidasi)
- 7.3 Zanjir oxirida catch yo'q
- 7.4 catch dan keyin "muvaffaqiyat"
- 7.5 Tayyor Promise'ni new Promise ga o'rash
- 8. Mashqlar
- 1-mashq (oson): Yo'qolgan qiymat
- 2-mashq (o'rta): Choy zanjiri
- 3-mashq (qiyin): Qayta urinish
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript Promise zanjiri: then qaytaradigan yangi Promise
Qisqacha:
thendoim yangi Promise qaytaradi. Callback qiymat qaytarsa — yangi Promise shu qiymat bilan bajariladi; Promise qaytarsa — zanjir uni kutadi;throwqilsa — rad etiladi. Shuning uchun asinxron qadamlarni ichma-ich emas, tekis yozish mumkin:stolTop(4).then(bronQil).then(chekYoz).catch(...). Xato zanjir bo'ylab pastga — eng yaqincatchgacha — oqadi.
Bu darsda
thennima qaytarishini va keyingithenqaysi qiymatni olishini aniq ayta olasiz.- Callback hell piramidasini tekis Promise zanjiriga aylantira olasiz.
- Xato zanjir bo'ylab qanday tarqalishini,
catchdan keyin zanjir qanday davom etishini tushuntira olasiz. returnni unutish va ichma-ichthenxatolarini tanib, tuzata olasiz.- Ikki zanjir aralash bo'lganda chiqish tartibini microtask navbati bo'yicha ayta olasiz.
Oldin bilishingiz kerak: Promise asoslari, Callbacklar va callback hell, Event loop.
1. Nega bu kerak?
Callbacklar va callback hell darsida «Bahor»dagi bron to'rt qadamdan iborat edi: stol topish, bron qilish, buyurtma berish, chek yozish. Har qadam oldingisining natijasiga muhtoj, va kod o'ngga surilib, piramidaga aylangan edi. O'shanda "Promise bilan bu to'rt qadam to'rt tekis qatorga aylanadi" degan edik.
Promise asoslari da bitta asinxron ishni Promise bilan yozishni o'rgandik. Lekin to'rt qadamni-chi? Birinchi fikr — har then ichida keyingi funksiyani chaqirish:
stolTop(4).then((stol) => {
bronQil(stol).then((bron) => {
buyurtmaBer(bron).then((buyurtma) => {
// ...
});
});
});Bu yana o'sha piramida, faqat then bilan. Promise'ning haqiqiy kuchi boshqa joyda: then o'zi ham Promise qaytaradi. Bugun shu bitta fakt piramidani qanday yo'q qilishini ko'ramiz.
O'xshatish. «Bahor»da osh tayyorlash — konveyer: sabzi to'g'raladi, keyin qovuriladi, keyin guruch solinadi. Har bosqichdagi oshpaz oldingisidan tayyor narsani oladi va o'z ishini qilib, keyingisiga uzatadi. Hech kim boshqasining ichiga kirmaydi — har biri faqat "kirdi" va "chiqdi" ni biladi. Promise zanjiri — xuddi shunday konveyer.
2. then yangi Promise qaytaradi
2.1 Ikkita har xil Promise
Tekshirib ko'ramiz: then nima qaytaradi?
const narx = new Promise((resolve) => resolve(35000));
const ikkiPorsiya = narx.then((n) => n * 2);
console.log(narx === ikkiPorsiya);
console.log(ikkiPorsiya);
setTimeout(() => console.log(ikkiPorsiya), 0);Konsolda:
false
Promise { <pending> }
Promise { 70000 }Uchta narsani ko'ramiz:
ikkiPorsiya—narxning o'zi emas, boshqa Promise.- Darhol chiqarganimizda u hali kutilmoqda.
thencallback'i microtask'da ishlaydi (Promise asoslari) — sinxron kod paytida u hali bajarilmagan. - Uchinchi qatorni
setTimeout(..., 0)bilan keyinga surdik. Taymer callback'i — task, u microtask'lardan keyin ishlaydi (Event loop). Shu paytga kelib callback ishlab bo'lgan vaikkiPorsiyauning qaytargan qiymati bilan bajarilgan:35000 * 2, ya'ni70000.
Demak then callback'idagi return — oddiy return emas. U keyingi Promise'ning natijasi bo'ladi.
2.2 Qiymat qaytarish: konveyer
Natija yangi Promise'da bo'lgani uchun unga yana then ulash mumkin. Va yana, va yana:
const narx = new Promise((resolve) => resolve(35000));
narx
.then((n) => n * 2)
.then((jami) => jami - 5000)
.then((tolov) => `To'lov: ${tolov} so'm`)
.then((matn) => console.log(matn));Konsolda:
To'lov: 65000 so'mHar then oldingisidan qiymat oladi va o'zinikini qaytaradi: 35000 → 70000 (ikki porsiya) → 65000 (5 000 chegirma) → matn. Oxirgi then faqat chiqaradi. U hech narsa qaytarmaydi, shuning uchun uning Promise'i undefined bilan bajariladi — lekin unga hech kim qaramaydi.
Qatorlarning joylashuvi — kelishuv: zanjir boshi alohida qatorda, har then yangi qatorda, ikki bo'shliq ichkarida. Bu bitta uzun ifoda, faqat o'qish uchun bo'lingan.
Tekshirib ko'ring: Zanjirdagi ikkinchi qatorni
.then((jami) => { jami - 5000; })deb o'zgartirsak, oxirida nima chiqadi?
Javob
To'lov: undefined so'm. Jingalak qavslar bilan yozilgan funksiyada return yo'q — u undefined qaytaradi. Keyingi then aynan shu undefined ni oladi. Qavssiz shakl (jami) => jami - 5000 esa qiymatni o'zi qaytaradi.
2.3 Promise qaytarish: zanjir kutadi
Endi eng muhim qoida. Callback Promise qaytarsa, yangi Promise darhol bajarilmaydi. U qaytarilgan Promise hal bo'lishini kutadi va uning natijasini oladi.
Buni ko'rish uchun kichik yordamchi yozamiz — kut(ms): ms millisekunddan keyin bajariladigan Promise.
function kut(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
console.log("Choy qo'yildi");
kut(300)
.then(() => {
console.log("Suv qaynadi");
return kut(200);
})
.then(() => {
console.log("Choy damlandi");
return "ko'k choy";
})
.then((choy) => console.log(`Tayyor: ${choy}`));Konsolda:
Choy qo'yildi
Suv qaynadi
Choy damlandi
Tayyor: ko'k choysetTimeout(resolve, ms) — resolve ning o'zini taymer callback'i qilib beramiz: vaqt kelganda Promise bajariladi. Qiymati undefined, chunki bizga faqat "vaqt o'tdi" degan xabar kerak.
Zanjirga qarang. Birinchi then "Suv qaynadi" deb yozadi va kut(200) ni qaytaradi. Ikkinchi then darhol ishlamaydi: zanjir 200 ms kutadi. Faqat kut(200) bajarilgach "Choy damlandi" chiqadi. Vaqt bo'yicha shunday:
| Vaqt | Nima bo'ldi |
|---|---|
| 0 ms | "Choy qo'yildi", kut(300) taymeri qo'yildi |
| ~300 ms | "Suv qaynadi", kut(200) qaytarildi — zanjir kutadi |
| ~500 ms | "Choy damlandi", keyin darhol "Tayyor: ko'k choy" |
Oxirgi ikki qator orasida kutish yo'q: ikkinchi then Promise emas, oddiy satr qaytardi.
kut — bu qismda tez-tez ishlatadigan yordamchimiz. Uning "rasmiy" ko'rinishi va o'xshash vositalar — Promise yordamchilari darsida.
2.4 Qoida bitta jadvalda
then qaytaradigan yangi Promise'ning taqdiri faqat callback'ga bog'liq:
| Callback nima qildi | Yangi Promise |
|---|---|
qiymat qaytardi (return 70000) |
shu qiymat bilan bajarildi |
| hech narsa qaytarmadi | undefined bilan bajarildi |
Promise qaytardi (return kut(200)) |
o'sha Promise'ni kutadi, uning natijasini oladi |
throw qildi |
o'sha xato bilan rad etildi |
Oxirgi qatorni «Xato zanjir bo'ylab oqadi» bo'limida ko'ramiz.
Tekshirib ko'ring:
kut(100).then(() => kut(500)).then(() => console.log("tamom"))— "tamom" taxminan necha millisekunddan keyin chiqadi?
Javob
Taxminan 600 ms. Birinchi then kut(500) ni qaytaradi, shuning uchun zanjir uni kutadi: 100 + 500. Agar return bo'lmasa (() => { kut(500); }), "tamom" 100 ms da chiqardi — buni «Ko'p uchraydigan xatolar» da ko'ramiz.
3. Callback hell → tekis zanjir
3.1 To'rt qadam — to'rt qator
Endi va'dani bajaramiz. To'rt funksiyani Promise qaytaradigan qilib yozamiz va zanjirga ulaymiz:
function stolTop(odam) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (odam > 20) reject(new Error("Bunday katta stol yo'q"));
else resolve(5);
}, 100);
});
}
function bronQil(stol) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${stol}-stol, 19:00`), 100);
});
}
function buyurtmaBer(bron) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${bron}, osh`), 100);
});
}
function chekYoz(buyurtma) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${buyurtma}: 35000 so'm`), 100);
});
}
stolTop(4)
.then((stol) => bronQil(stol))
.then((bron) => buyurtmaBer(bron))
.then((buyurtma) => chekYoz(buyurtma))
.then((chek) => console.log("Chek:", chek))
.catch((xato) => console.log("Xato:", xato.message));Konsolda:
Chek: 5-stol, 19:00, osh: 35000 so'mCallback'li versiyani eslang: to'rt daraja ichkariga, to'rt marta if (xato). Bu yerda — to'rt tekis qator va bitta catch. Har then keyingi funksiyaning Promise'ini qaytaradi, zanjir uni kutadi va natijani pastga uzatadi.
Natija bir xil — Chek: 5-stol, 19:00, osh: 35000 so'm. Ish tartibi ham bir xil: har qadam oldingisi tugagach boshlanadi. Faqat kod endi yuqoridan pastga o'qiladi — xuddi sinxron kod kabi.
3.2 Yanada qisqa
(stol) => bronQil(stol) — "stolni olib, bronQil ga ber". Buni qisqaroq yozish mumkin: funksiyaning o'zini, qavssiz beramiz — Taymerlar darsidagi setTimeout(salomBer, 1000) kabi:
stolTop(4)
.then(bronQil)
.then(buyurtmaBer)
.then(chekYoz)
.then((chek) => console.log("Chek:", chek))
.catch((xato) => console.log("Xato:", xato.message));then(bronQil) — then natijani bronQil ning birinchi argumentiga beradi. Funksiya bitta parametr olsa — xavfsiz. Bir nechta parametrli funksiya bilan ehtiyot bo'ling: then faqat bitta argument beradi.
flowchart TD
A["stolTop(4)"] -->|"stol"| B["then(bronQil)"]
B -->|"bron"| C["then(buyurtmaBer)"]
C -->|"buyurtma"| D["then(chekYoz)"]
D -->|"chek"| E["then: Chek: ..."]
A -. "rad etildi" .-> X["catch: Xato: ..."]
B -.-> X
C -.-> X
D -.-> XDiagrammaga qarang: asosiy yo'l — to'g'ri chiziq. Xato bo'lsa, istalgan qadamdan bitta catch ga sakraladi (punktir strelkalar).
3.3 Zanjirni funksiyadan qaytaring
Real kodda bron jarayoni bir joyda yoziladi, natijasi esa boshqa joyda ishlatiladi: bir sahifa chekni ekranga chiqaradi, boshqasi SMS yuboradi. Buning uchun zanjirni funksiyaga o'raymiz va zanjirning o'zini qaytaramiz:
function kut(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function bronJarayoni(odam) {
return kut(100)
.then(() => {
if (odam > 20) throw new Error("Bunday katta stol yo'q");
return 5;
})
.then((stol) => `${stol}-stol, ${odam} kishi, 19:00`);
}
bronJarayoni(4)
.then((bron) => console.log("Ekranda:", bron))
.catch((xato) => console.log("Ekranda xato:", xato.message));
bronJarayoni(30)
.then((bron) => console.log("SMS:", bron))
.catch((xato) => console.log("SMS xato:", xato.message));Konsolda:
Ekranda: 5-stol, 4 kishi, 19:00
SMS xato: Bunday katta stol yo'qbronJarayoni o'zi catch qilmaydi — u xato bilan nima qilishni bilmaydi. Ekran xatoni qizil matn bilan, SMS xizmati esa boshqacha ko'rsatadi. Shuning uchun funksiya zanjirni return qiladi va qaror chaqiruvchida qoladi. Bu — Xato strategiyasi darsidagi "xatoni ushlay olmasang, yuqoriga uzat" qoidasining asinxron ko'rinishi.
Zanjir boshidagi kut(100) — server so'rovi o'rnida turibdi. Undan keyingi then ichida oddiy sinxron tekshiruv va throw — ular ham zanjirning bir qismi bo'ladi.
Tekshirib ko'ring: Yuqoridagi zanjirni
stolTop(25)bilan boshlasak,bronQilchaqiriladimi? Nima chiqadi?
Javob
Yo'q. stolTop(25) rad etiladi. Uchta then va oxirgi then ham o'tkazib yuboriladi, xato to'g'ri catch ga keladi: Xato: Bunday katta stol yo'q.
4. Bajarilish tartibi: har then — alohida microtask
Eslatma: microtask navbati — sinxron kod tugagach, taymerlardan oldin bo'shatiladigan navbat (Event loop). Navbatga birinchi tushgan birinchi ishlaydi.
Zanjirdagi har then callback'i alohida microtask. Keyingisi navbatga faqat oldingisi tugagach qo'yiladi. Ikki zanjir yonma-yon bo'lsa, ular navbatma-navbat ishlaydi. Tartibni oldindan ayting:
const osh = new Promise((resolve) => resolve("osh"));
const choy = new Promise((resolve) => resolve("choy"));
osh
.then((t) => console.log(`1. ${t} tayyor`))
.then(() => console.log("3. osh suzildi"));
choy
.then((t) => console.log(`2. ${t} tayyor`))
.then(() => console.log("4. choy quyildi"));
console.log("0. sinxron kod tugadi");Konsolda:
0. sinxron kod tugadi
1. osh tayyor
2. choy tayyor
3. osh suzildi
4. choy quyildiKo'pchilik "osh zanjiri birinchi yozilgan — demak 1 va 3 birga chiqadi" deb o'ylaydi. Yo'q: zanjirlar "aralashib" ketdi. Qadamma-qadam qarang — microtask navbatiga nima qachon tushishini kuzating:
Kalit joy — oltinchi qadam. «3» zanjirda «1» dan keyin turibdi, lekin u navbatga «1» tugagandagina tushadi. Shu paytda «2» allaqachon navbatda turgan edi. Shuning uchun zanjirlar bitta-bitta "qadam tashlaydi".
Bitta nozik detal: callback qiymat emas, Promise qaytarsa, zanjir uni "ochish" uchun yana ikki microtask sarflaydi. Aralash zanjirlarda bu tartibni yana o'zgartiradi. Buni Microtask va macrotask chuqur darsida qadamma-qadam ko'ramiz. Hozir bitta qoida yetarli: bog'liq qadamlar tartibi — zanjir bo'yicha, mustaqil zanjirlar orasidagi tartibga esa tayanmang.
Tekshirib ko'ring: Yuqoridagi kodda choy zanjiriga uchinchi
.then(() => console.log("5. choy ichildi"))qo'shsak, u qayerda chiqadi?
Javob
Eng oxirida — "4. choy quyildi" dan keyin. U «4» ning Promise'ini kutadi va navbatga faqat «4» tugagach tushadi. Shu paytda osh zanjirida navbatga qo'yiladigan hech narsa qolmagan.
5. Xato zanjir bo'ylab oqadi
5.1 Rad etilgan Promise then larni o'tkazib yuboradi
Zanjirning biror qadami rad etilsa, keyingi hamma then lar o'tkazib yuboriladi — xato eng yaqin catch ga yetguncha pastga tushadi:
function stolTop(odam) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (odam > 20) reject(new Error("Bunday katta stol yo'q"));
else resolve(5);
}, 100);
});
}
stolTop(25)
.then((stol) => {
console.log("bron qilinmoqda:", stol);
return `${stol}-stol`;
})
.then((bron) => console.log("buyurtma:", bron))
.catch((xato) => console.log("Xato:", xato.message))
.finally(() => console.log("Kassir bo'shadi"));Konsolda:
Xato: Bunday katta stol yo'q
Kassir bo'shadiIkkala then dan hech narsa chiqmadi. Xato try/catch dagi throw kabi ishlaydi: throw dan keyingi qatorlar bajarilmaydi, boshqaruv to'g'ri catch ga o'tadi (try / catch / finally). Zanjir — asinxron try bloki, catch — uning catch qismi.
5.2 then ichidagi throw
Jadvaldagi oxirgi qator: then callback'i throw qilsa, uning Promise'i rad etiladi. Bu zanjirda o'z xatolaringizni chiqarishning yo'li:
const narx = new Promise((resolve) => resolve(35000));
narx
.then((n) => {
if (n > 30000) {
throw new Error(`Hamyonda yetmaydi: ${n} so'm`);
}
return n;
})
.then((n) => console.log("To'landi:", n))
.catch((xato) => console.log("Xato:", xato.message));Konsolda:
Xato: Hamyonda yetmaydi: 35000 so'mthen ichidagi throw dastur to'xtatmadi — u keyingi Promise'ning rad etilishiga aylandi. Buni Callbacklar darsidagi taymer ichidagi throw bilan solishtiring: u yerda dastur yiqilgan edi. Promise callback'larni o'zining "himoya qobig'i" ichida chaqiradi va har xatoni ushlab, rad etishga aylantiradi.
Kutilmagan xatolar ham shunday ushlanadi: undefined.length kabi TypeError ham zanjir oxiridagi catch ga keladi.
Tekshirib ko'ring: Yuqoridagi kodda narx
25000bo'lsa, nima chiqadi?throwqatori bajariladimi?
Javob
To'landi: 25000. Shart (25000 > 30000) bajarilmaydi, throw ishlamaydi. Birinchi then 25000 ni qaytaradi, ikkinchi then uni oladi, catch esa jim o'tkazib yuboriladi.
5.3 catch dan keyin zanjir davom etadi
catch ham then kabi yangi Promise qaytaradi. Agar catch callback'i oddiy qiymat qaytarsa — zanjir tiklanadi: keyingi then lar yana ishlaydi.
function narxniSora(taom) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (taom === "osh") resolve(35000);
else reject(new Error(`Server javob bermadi: ${taom}`));
}, 100);
});
}
const ESKI_NARX = 28000;
narxniSora("lag'mon")
.catch((xato) => {
console.log("Ogohlantirish:", xato.message);
return ESKI_NARX;
})
.then((narx) => console.log(`Lag'mon: ${narx} so'm`));Konsolda:
Ogohlantirish: Server javob bermadi: lag'mon
Lag'mon: 28000 so'mServer javob bermadi, catch eski narxni qaytardi va zanjir "muvaffaqiyatli" davom etdi. Bu — xatodan tiklanish (recovery): zaxira qiymat, keshdagi ma'lumot, standart sozlama.
Lekin bu qobiliyat tuzoq ham bo'lishi mumkin. catch faqat xatoni chiqarib, hech narsa qaytarmasa — keyingi then undefined bilan ishlaydi: Lag'mon: undefined so'm. catch ni qayerga qo'yishni o'ylab tanlang:
- Oxirida — "biror narsa xato bo'lsa, butun ish bekor". Eng ko'p uchraydigan holat.
- O'rtada — "shu qadam xato bersa, zaxira bilan davom et". Faqat zaxira qiymat qaytarganda.
Xatoni ushlab, lekin uni o'zingiz hal qila olmasangiz — qayta throw qiling. Shunda u keyingi catch ga boradi.
5.4 then(ok, xato) va then(ok).catch(xato) farqi
Promise asoslari da then ning ikkinchi argumenti haqida "nozik farq bor" degan edik. Mana u:
const narx = new Promise((resolve) => resolve(35000));
narx
.then(
(n) => { throw new Error("1-zanjirda xato"); },
(xato) => console.log("ikkinchi argument:", xato.message),
)
.catch((xato) => console.log("tashqi catch:", xato.message));
narx
.then((n) => { throw new Error("2-zanjirda xato"); })
.catch((xato) => console.log("catch:", xato.message));Konsolda:
tashqi catch: 1-zanjirda xato
catch: 2-zanjirda xatoBirinchi zanjirda "ikkinchi argument" umuman chiqmadi. U faqat oldingi Promise'ning — narx ning — xatosini ko'radi. O'zi bilan yonma-yon turgan birinchi argumentdagi xato esa undan "o'tib ketdi" va pastdagi catch ga tushdi. Agar u yerda catch bo'lmaganda — xato ushlanmay qolardi.
catch esa zanjirda keyin turadi va undan oldingi hamma narsani, jumladan then ichidagi xatoni ham ushlaydi. Shuning uchun qoida: then(ok, xato) o'rniga then(ok).catch(xato) yozing.
Tekshirib ko'ring:
p.catch(() => "zaxira").then((x) => console.log(x))—pbajarilgan bo'lsa ("osh"bilan) nima chiqadi? Rad etilgan bo'lsa-chi?
Javob
Bajarilgan bo'lsa — osh: catch bajarilgan Promise'ni o'zgartirmasdan o'tkazib yuboradi. Rad etilgan bo'lsa — zaxira: catch xatoni ushlab, zaxira qiymat qaytardi va zanjir tiklandi.
6. finally zanjirda
finally zanjirning istalgan joyida turishi mumkin va u natijani o'zgartirmaydi — qiymatni ham, xatoni ham o'zidan o'tkazib yuboradi:
const narx = new Promise((resolve) => resolve(35000));
narx
.finally(() => console.log("Yuklanish belgisi yashirildi"))
.then((n) => console.log("Narx:", n));Konsolda:
Yuklanish belgisi yashirildi
Narx: 35000finally callback'i hech narsa olmadi, lekin 35000 uning ustidan o'tib, keyingi then ga yetdi. finally ichida return qilsangiz ham, u e'tiborsiz qoladi. Bitta istisno bor: finally ichida throw qilinsa (yoki u rad etiladigan Promise qaytarsa), zanjir shu yangi xato bilan rad etiladi. Shuning uchun finally dagi kod sodda va xavfsiz bo'lsin — try/finally dagi kabi.
Tekshirib ko'ring: Zanjir
.catch(...)bilan emas,.finally(...)bilan tugadi, oldingi qadam esa rad etildi. Xato ushlandimi?
Javob
Yo'q. finally xatoni o'zidan o'tkazib yuboradi — uning callback'i ishlaydi, lekin zanjir hamon rad etilgan holda qoladi. Xato ushlanmagan rad etilish bo'ladi. Ushlash uchun catch kerak: odatda .catch(...).finally(...) tartibida.
7. Ko'p uchraydigan xatolar
7.1 return ni unutish
Promise zanjiridagi eng ko'p uchraydigan xato. Ikki ko'rinishi bor.
Birinchisi — qiymat yo'qoladi:
const narx = new Promise((resolve) => resolve(35000));
narx
.then((n) => {
n * 2;
})
.then((jami) => console.log("Jami:", jami));Konsolda:
Jami: undefinedJingalak qavsli arrow funksiyada return yozilmasa — funksiya undefined qaytaradi (Function expression va arrow funksiyalar). Qavssiz shakl (n) => n * 2 esa qiymatni o'zi qaytaradi.
Ikkinchisi — xavfliroq: zanjir kutmaydi.
function kut(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
kut(100)
.then(() => {
kut(300).then(() => console.log("Choy damlandi"));
})
.then(() => console.log("Choy quyildi"));Konsolda:
Choy quyildi
Choy damlandiChoy damlanmasdan quyildi! Birinchi then kut(300) ni qaytarmadi — u undefined qaytardi. Zanjir kutadigan hech narsa ko'rmadi va darhol keyingi qadamga o'tdi. Qadamma-qadam kuzating:
Damlash zanjirdan tashqarida, "o'z-o'zicha" davom etdi. Uning xatosi ham hech qanday catch ga yetmaydi. Tuzatish: then ichida boshlangan har asinxron ishni return qiling: return kut(300).then(...). Shunda uchinchi qadamda 1-then'ning Promise'i 300 ms kutadi va "Choy quyildi" "Choy damlandi" dan keyin chiqadi.
7.2 Ichma-ich then (Promise piramidasi)
stolTop(4).then((stol) => {
return bronQil(stol).then((bron) => {
return chekYoz(bron).then((chek) => {
console.log(chek);
});
});
});Bu kod ishlaydi — return lar joyida. Lekin bu o'sha callback hell: Promise faqat nomigagina ishlatilgan. Tuzatish: har then dan Promise'ni qaytaring va keyingi then ni tashqarida, zanjirga ulang — «Callback hell → tekis zanjir» bo'limidagi kabi.
Odamlar ichma-ich yozishning bitta "sababi" bor: pastki qadamga yuqoridagi natija kerak. Masalan, chekda ham stol raqami, ham buyurtma bo'lsin. Tekis zanjirda stol faqat birinchi then da ko'rinadi. Ikki yechim:
- Natijalarni obyektga yig'ib uzatish:
.then((stol) => bronQil(stol).then((bron) => ({ stol, bron })))— bitta qisqa ichkithen, piramida emas. - Keyingi darsdagi
async/await— u yerda hamma natija oddiy o'zgaruvchida turadi va bu muammo umuman yo'q.
7.3 Zanjir oxirida catch yo'q
catch siz zanjirdagi har qanday xato — ushlanmagan rad etilish: Node'da dastur to'xtaydi, brauzerda Uncaught (in promise) (Promise asoslari). Qoida: zanjir yoki qaytariladi (uni chaqirgan kod catch qiladi), yoki catch bilan tugaydi. Uchinchi yo'l yo'q.
7.4 catch dan keyin "muvaffaqiyat"
catch xatoni chiqardi-yu, hech narsa qaytarmadi — keyingi then lar undefined bilan ishlaydi va "Bron tasdiqlandi!" deb yozib yuboradi. Tuzatish: zanjirni to'xtatish kerak bo'lsa, catch ni oxiriga qo'ying yoki uning ichida xatoni qayta throw qiling.
7.5 Tayyor Promise'ni new Promise ga o'rash
Zanjirni o'rgangan odam ba'zan shunday yozadi:
function stolTop(odam) {
return new Promise((resolve, reject) => {
kut(100)
.then(() => resolve(5))
.catch((xato) => reject(xato));
});
}kut(100) allaqachon Promise qaytaradi — uni yana new Promise ga o'rash ortiqcha. Bu kod uzunroq va xavfliroq: then ichida biror narsa xato bersa-yu, reject ga ulanishni unutsangiz, tashqi Promise abadiy kutib qoladi. Ingliz tilida bu "explicit construction anti-pattern" ("ortiqcha konstruktor") deyiladi. Anti-pattern — ko'p uchraydigan, lekin yomon yechim. Tuzatish: mavjud zanjirni qaytaring — return kut(100).then(() => 5);. new Promise faqat callback'li yoki taymerli kodni birinchi marta Promise'ga aylantirish uchun kerak.
8. Mashqlar
1-mashq (oson): Yo'qolgan qiymat
Sardorning kodi Chegirmali narx: undefined so'm chiqaryapti. Xatoni toping va tuzating — Chegirmali narx: 31500 so'm chiqsin.
const narx = new Promise((resolve) => resolve(35000));
narx
.then((n) => {
const chegirma = n * 0.1;
n - chegirma;
})
.then((yangi) => console.log(`Chegirmali narx: ${yangi} so'm`));Yechim
const narx = new Promise((resolve) => resolve(35000));
narx
.then((n) => {
const chegirma = n * 0.1;
return n - chegirma;
})
.then((yangi) => console.log(`Chegirmali narx: ${yangi} so'm`));Konsolda:
Chegirmali narx: 31500 so'mBirinchi then hisobladi, lekin natijani qaytarmadi. Jingalak qavsli funksiyada return shart. Keyingi then aynan shu return qiymatini oladi.
2-mashq (o'rta): Choy zanjiri
Callbacklar darsidagi uch qadamli ko'k choyni Promise bilan qayta yozing. suvQaynat(), choySol(suv), damla(choynak) — har biri 100 ms dan keyin bajariladigan Promise qaytarsin (natijalar: "qaynoq suv", "<suv> + ko'k choy", "<choynak> (damlandi)"). Ularni tekis zanjirga ulang, oxirida catch bo'lsin. Natija: Tayyor: qaynoq suv + ko'k choy (damlandi).
Yechim
function suvQaynat() {
return new Promise((resolve) => {
setTimeout(() => resolve("qaynoq suv"), 100);
});
}
function choySol(suv) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${suv} + ko'k choy`), 100);
});
}
function damla(choynak) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${choynak} (damlandi)`), 100);
});
}
suvQaynat()
.then(choySol)
.then(damla)
.then((choy) => console.log("Tayyor:", choy))
.catch((xato) => console.log("Xato:", xato.message));Konsolda:
Tayyor: qaynoq suv + ko'k choy (damlandi)Callback'li yechimda uchta nomli funksiya (suvQaynadi, choySolindi, damlandi) va har birida if (xato) kerak edi. Bu yerda zanjir o'zi "keyingi qadam" ni biladi, xato esa bitta catch da.
3-mashq (qiyin): Qayta urinish
Oshxona serveri ba'zan javob bermaydi. kurs/mashqlar/11/06-zanjir/qaytaUrin.mjs faylida qaytaUrin(ish, marta) funksiyasini yozing: ish — Promise qaytaradigan funksiya. qaytaUrin uni chaqirsin; rad etilsa — marta tugaguncha qayta chaqirsin; urinishlar tugasa — oxirgi xato bilan rad etilsin. Ishora: ish().catch(...) ichida qaytaUrin ni o'zini qaytaring — catch Promise qaytarsa, zanjir uni kutadi.
Sinov uchun ishonchsizServer tayyor: u birinchi ikki chaqiruvda rad etadi, uchinchisida bajariladi.
let chaqiruv = 0;
function ishonchsizServer() {
chaqiruv++;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (chaqiruv < 3) reject(new Error(`${chaqiruv}-urinish xato`));
else resolve(`${chaqiruv}-urinishda narx: 28000`);
}, 100);
});
}Yechim
let chaqiruv = 0;
function ishonchsizServer() {
chaqiruv++;
return new Promise((resolve, reject) => {
setTimeout(() => {
if (chaqiruv < 3) reject(new Error(`${chaqiruv}-urinish xato`));
else resolve(`${chaqiruv}-urinishda narx: 28000`);
}, 100);
});
}
function qaytaUrin(ish, marta) {
return ish().catch((xato) => {
console.log("Xato:", xato.message);
if (marta <= 1) {
throw xato;
}
return qaytaUrin(ish, marta - 1);
});
}
qaytaUrin(ishonchsizServer, 5)
.then((natija) => console.log(natija))
.catch((xato) => console.log("Taslim:", xato.message));Konsolda:
Xato: 1-urinish xato
Xato: 2-urinish xato
3-urinishda narx: 28000catch ichida ikki yo'l bor. Urinishlar tugagan bo'lsa — throw xato: zanjir rad etiladi va tashqaridagi catch "Taslim" deb yozadi. Aks holda — return qaytaUrin(...): yangi urinishning Promise'i qaytariladi, zanjir uni kutadi. marta ni 2 qilib sinang — Taslim: 2-urinish xato chiqadi.
Bu rekursiya, lekin stek o'smaydi: har yangi urinish taymer va microtask'dan, bo'sh stekdan boshlanadi. Real ilovalarda urinishlar orasiga o'sib boradigan kutish qo'yiladi — eksponensial kutish (Taymerlar darsida ko'rgansiz). Qayta urinish, vaqt chegarasi va boshqa tayyor "retseptlar" — Asinxron naqshlar darsida.
git add 11/06-zanjir/qaytaUrin.mjs
git commit -m "11/06: qaytaUrin — Promise bilan qayta urinish"9. Real ishda
- Eski va kutubxona kodi.
async/awaitdan oldin (2017 gacha) hamma asinxron kodthenzanjirlari bilan yozilgan. Ko'p kutubxonalarning hujjatlarida hamon.then().catch()misollari turibdi. - Kichik qayta ishlash. Bitta qatorli o'zgartirish uchun zanjir qulay:
narxniSora("osh").then((n) => n * 2). Tarmoq so'rovida ham shunday shakl ko'p uchraydi: javobni olib, uni JSON'ga aylantirish —fetchasoslari da ko'rasiz. - Kod sharhi. Tajribali dasturchi zanjirli kodda birinchi navbatda ikki narsani qidiradi: har
thenichida boshlangan asinxron ishreturnqilinganmi va zanjircatchbilan tugaydimi (yoki qaytariladimi). Bu ikki xato loglarda "ba'zan" chiqadigan, qayta takrorlash qiyin muammolarning eng ko'p uchraydigan sababi. - Xatodan tiklanish. Zaxira qiymat (
catch(() => keshdagiNarx)), qayta urinish (3-mashq) — real ilovalarda har kuni. - Intervyu. "
thennima qaytaradi?", "thenichidareturnunutilsa nima bo'ladi?", "then(a, b)vathen(a).catch(b)farqi?", ikki zanjirning chiqish tartibi — klassik savollar.
Xulosa
then(vacatch,finally) doim yangi Promise qaytaradi — shuning uchun ularni zanjirga ulash mumkin.- Callback qiymat qaytarsa — keyingi
thenuni oladi; Promise qaytarsa — zanjir uni kutadi;throwqilsa — zanjir rad etiladi. - Callback hell → tekis zanjir: har qadam keyingi Promise'ni qaytaradi, oxirida bitta
catch. - Rad etish keyingi
thenlarni o'tkazib, eng yaqincatchga tushadi;catchqiymat qaytarsa — zanjir tiklanadi. - Har
then— alohida microtask; mustaqil zanjirlar navbatma-navbat qadam tashlaydi. - Eng ko'p xato —
thenichidareturnni unutish: qiymat yo'qoladi va zanjir kutmaydi.
Keyingi dars: async / await — zanjirni sinxron kod kabi yozish: const stol = await stolTop(4);, xatolar esa tanish try/catch bilan.
Manbalar
- MDN: "Using promises" (Chaining, Error handling), "Promise.prototype.then()" — developer.mozilla.org
- ECMAScript spetsifikatsiyasi: "Promise.prototype.then", "PromiseResolveThenableJob" — tc39.es/ecma262
- Jake Archibald, "JavaScript Promises: an introduction" (Chaining, Error handling) — web.dev
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!