17-bo‘lim
Promise va async/await
Promise nima, then va catch, async/await sintaksisi, parallel bajarish, Promise.all va xatolarni to'g'ri ushlash.
Ushbu bo‘lim mundarijasi
Promise - "kelajakda qiymat bo'ladi" degan va'da. U callback
jahannamini yechadi va try/catch ni asinxron kodda ishlatadi.
Promise yaratish #
function kutish(ms, qiymat) {
return new Promise((bajarildi) => {
setTimeout(() => bajarildi(qiymat), ms);
});
}
function xatoli(ms) {
return new Promise((bajarildi, rad) => {
setTimeout(() => rad(new Error("bajarilmadi")), ms);
});
}
kutish(10, "natija").then((q) => console.log("then:", q));
xatoli(10).catch((x) => console.log("catch:", x.message));
console.log("Sinxron kod avval");
Sinxron kod avval
then: natija
catch: bajarilmadi
Zanjir #
function kutish(ms, qiymat) {
return new Promise((bajarildi) => setTimeout(() => bajarildi(qiymat), ms));
}
kutish(5, 10)
.then((son) => {
console.log("1-qadam:", son);
return son * 2;
})
.then((son) => {
console.log("2-qadam:", son);
return kutish(5, son + 5); // promise qaytarsak - kutiladi
})
.then((son) => {
console.log("3-qadam:", son);
})
.finally(() => {
console.log("finally - har doim");
});
1-qadam: 10
2-qadam: 20
3-qadam: 25
finally - har doim
then ichida return qilishni unutmangkutish(5, 1)
.then((s) => {
kutish(5, s + 1); // return YO'Q!
})
.then((s) => console.log(s)); // undefined
return bo'lmasa, keyingi then kutmaydi va undefined
oladi.
Bu callback jahannamining Promise dagi ko'rinishi - eng ko'p uchraydigan xato.
async/await da bu muammo yo'q, chunki await majburan
kutadi. Shuning uchun zamonaviy kodda then zanjiri kamdan-kam
ishlatiladi.
async va await #
function kutish(ms, qiymat) {
return new Promise((bajarildi) => setTimeout(() => bajarildi(qiymat), ms));
}
async function bajarish() {
console.log("Boshlandi");
const birinchi = await kutish(5, 10);
console.log("1-natija:", birinchi);
const ikkinchi = await kutish(5, birinchi * 2);
console.log("2-natija:", ikkinchi);
return ikkinchi + 5;
}
bajarish().then((natija) => console.log("Yakuniy:", natija));
console.log("Sinxron kod");
Boshlandi
Sinxron kod
1-natija: 10
2-natija: 20
Yakuniy: 25
async funksiya har doim Promise qaytaradiasync function oddiy() {
return 42; // son qaytardik
}
console.log(oddiy()); // Promise { 42 } - Promise!
oddiy().then((n) => console.log(n)); // 42
Ichida await bo'lmasa ham, async funksiyaning natijasi
har doim Promise ga o'raladi.
Shuning uchun uni chaqirganda ham await yoki .then() kerak:
const natija = await oddiy(); // 42
Teskarisi ham to'g'ri: await oddiy qiymat bilan ham
ishlaydi - u shunchaki uni Promise ga o'raydi:
const son = await 42; // 42
try/catch endi ishlaydi #
function xatoli(ms) {
return new Promise((b, rad) => {
setTimeout(() => rad(new Error("server javob bermadi")), ms);
});
}
async function bajarish() {
try {
const natija = await xatoli(5);
console.log("Bu chiqmaydi:", natija);
} catch (xato) {
console.log("Ushlandi:", xato.message);
} finally {
console.log("Tozalash bajarildi");
}
}
bajarish();
Ushlandi: server javob bermadi
Tozalash bajarildi
async/await ning asosiy afzalligi16-bo'limda try/catch asinxron callback xatosini
ushlamasligini ko'rdik.
await bu muammoni hal qiladi: u funksiyani "to'xtatib turadi"
va xato kelganda uni sinxron kod kabi tashlaydi.
Natijada asinxron kod sinxron kabi o'qiladi:
try {
const foydalanuvchi = await olish(id);
const buyurtmalar = await buyurtmalar(foydalanuvchi.id);
korsatish(buyurtmalar);
} catch (xato) {
xatoKorsatish(xato);
}
Bitta try/catch butun zanjirni qamrab oladi.
Ketma-ket va parallel #
function kutish(ms, qiymat) {
return new Promise((b) => setTimeout(() => b(qiymat), ms));
}
async function ketmaKet() {
const boshi = Date.now();
const a = await kutish(30, "a");
const b = await kutish(30, "b");
const c = await kutish(30, "c");
return { natija: [a, b, c], vaqt: Date.now() - boshi };
}
async function parallel() {
const boshi = Date.now();
const [a, b, c] = await Promise.all([
kutish(30, "a"),
kutish(30, "b"),
kutish(30, "c")
]);
return { natija: [a, b, c], vaqt: Date.now() - boshi };
}
(async () => {
const k = await ketmaKet();
const p = await parallel();
console.log("Ketma-ket natija:", k.natija.join(", "));
console.log("Parallel natija :", p.natija.join(", "));
console.log("Ketma-ket 80 ms dan ko'pmi?:", k.vaqt > 80);
console.log("Parallel 60 ms dan kammi? :", p.vaqt < 60);
console.log("Parallel tezroqmi? :", p.vaqt < k.vaqt);
})();
Ketma-ket natija: a, b, c
Parallel natija : a, b, c
Ketma-ket 80 ms dan ko'pmi?: true
Parallel 60 ms dan kammi? : true
Parallel tezroqmi? : true
await - eng ko'p uchraydigan sekinlik// SEKIN - har biri navbat kutadi
const natijalar = [];
for (const id of idlar) {
natijalar.push(await malumotOlish(id));
}
100 ta so'rov × 100 ms = 10 soniya.
// TEZ - hammasi bir vaqtda boshlanadi
const natijalar = await Promise.all(idlar.map((id) => malumotOlish(id)));
100 ta so'rov parallel = ~100 ms.
Qachon ketma-ket kerak:
- Keyingi so'rov oldingisining natijasiga bog'liq bo'lsa;
- Server so'rovlar sonini cheklagan bo'lsa;
- Tartib muhim bo'lsa (lekin
Promise.alltartibni saqlaydi!).
Promise.all natijalarni kirish tartibida qaytaradi -
qaysi biri tez tugashidan qat'i nazar.
Promise.all va uning qarindoshlari #
function kutish(ms, qiymat) {
return new Promise((b) => setTimeout(() => b(qiymat), ms));
}
function xato(ms, xabar) {
return new Promise((b, rad) => setTimeout(() => rad(new Error(xabar)), ms));
}
(async () => {
// all - bittasi yiqilsa, hammasi yiqiladi
try {
await Promise.all([kutish(5, "a"), xato(5, "yiqildi"), kutish(5, "c")]);
} catch (x) {
console.log("all -> xato:", x.message);
}
// allSettled - hammasining natijasini beradi
const natijalar = await Promise.allSettled([
kutish(5, "a"),
xato(5, "yiqildi")
]);
console.log("allSettled:", natijalar.map((n) => n.status).join(", "));
// race - birinchi tugagani (xato bo'lsa ham)
const tez = await Promise.race([kutish(5, "tez"), kutish(50, "sekin")]);
console.log("race ->", tez);
// any - birinchi MUVAFFAQIYATLI
const birinchi = await Promise.any([xato(5, "yiqildi"), kutish(20, "ishladi")]);
console.log("any ->", birinchi);
})();
all -> xato: yiqildi
allSettled: fulfilled, rejected
race -> tez
any -> ishladi
| Metod | Qachon tugaydi | Xato bo'lsa |
|---|---|---|
all | Hammasi tugaganda | Darhol yiqiladi |
allSettled | Hammasi tugaganda | Hech qachon yiqilmaydi |
race | Birinchi tugaganda | Birinchisi xato bo'lsa - yiqiladi |
any | Birinchi muvaffaqiyat | Hammasi yiqilsa - yiqiladi |
allSettled - qismiy muvaffaqiyat uchunUch xil ma'lumot yuklayotgan sahifani tasavvur qiling. Bittasi
yiqilsa, Promise.all bilan hammasi yo'qoladi.
allSettled esa har birining holatini beradi:
const natijalar = await Promise.allSettled([
profilOlish(),
xabarlarOlish(),
sozlamalarOlish()
]);
for (const n of natijalar) {
if (n.status === "fulfilled") {
korsatish(n.value);
} else {
xatoKorsatish(n.reason);
}
}
Har element ikki shakldan biri bo'ladi:
{ status: "fulfilled", value: ... }
{ status: "rejected", reason: ... }
Foydalanuvchi kamida ishlagan qismini ko'radi.
Ushlamagan rad etish #
// Promise yaratildi, lekin catch qo'yilmadi
const xatoli = new Promise((b, rad) => {
setTimeout(() => rad(new Error("hech kim ushlamadi")), 5);
});
// Node bunday holatni aniqlaydi
process.on("unhandledRejection", (sabab) => {
console.log("Ushlanmagan rad etish:", sabab.message);
});
setTimeout(() => console.log("Dastur davom etdi"), 30);
Ushlanmagan rad etish: hech kim ushlamadi
Dastur davom etdi
async function saqlash() {
await serverga Yuborish(); // xato bo'lsa nima bo'ladi?
}
saqlash(); // catch yo'q - xato yo'qoladi
Node.js da bu dasturni to'xtatadi (versiya 15 dan boshlab). Brauzerda esa konsolga chiqadi va sezilmay qolishi mumkin.
To'g'ri variantlar:
saqlash().catch((x) => console.error("Saqlab bo'lmadi:", x));
try {
await saqlash();
} catch (x) {
xatoKorsatish(x);
}
Global tutqich ham qo'ying - kutilmagan xatolarni jurnalga yozish uchun:
// Brauzerda
window.addEventListener("unhandledrejection", (h) => {
jurnalgaYozish(h.reason);
h.preventDefault(); // konsolga chiqmasin
});
Amaliy naqsh: vaqt chegarasi #
function kutish(ms, qiymat) {
return new Promise((b) => setTimeout(() => b(qiymat), ms));
}
function chegaraBilan(promise, ms) {
const chegara = new Promise((b, rad) => {
setTimeout(() => rad(new Error(`${ms} ms dan oshdi`)), ms);
});
return Promise.race([promise, chegara]);
}
(async () => {
try {
const tez = await chegaraBilan(kutish(10, "ulgurdi"), 50);
console.log("Tez so'rov :", tez);
} catch (x) {
console.log("Tez so'rov xato:", x.message);
}
try {
await chegaraBilan(kutish(100, "kech"), 20);
console.log("Bu chiqmaydi");
} catch (x) {
console.log("Sekin so'rov:", x.message);
}
})();
Tez so'rov : ulgurdi
Sekin so'rov: 20 ms dan oshdi
Ketma-ket qayta urinish #
function kutish(ms) {
return new Promise((b) => setTimeout(b, ms));
}
let urinishlar = 0;
async function beqarorAmal() {
urinishlar++;
if (urinishlar < 3) {
throw new Error(`${urinishlar}-urinish yiqildi`);
}
return "muvaffaqiyat";
}
async function qaytaUrinish(amal, maks = 5, kechikish = 5) {
for (let i = 1; i <= maks; i++) {
try {
return await amal();
} catch (xato) {
console.log(` ${xato.message}`);
if (i === maks) {
throw xato;
}
await kutish(kechikish * i); // har safar ko'proq kutamiz
}
}
}
(async () => {
const natija = await qaytaUrinish(beqarorAmal);
console.log("Natija:", natija);
console.log("Urinishlar soni:", urinishlar);
})();
1-urinish yiqildi
2-urinish yiqildi
Natija: muvaffaqiyat
Urinishlar soni: 3
Yuqoridagi misolda kechikish har urinishda ortadi: 5, 10, 15 ms.
Real tizimlarda odatda ikki barobar oshiriladi:
await kutish(kechikish * 2 ** i); // 100, 200, 400, 800 ms
Nima uchun: server yuklangan bo'lsa, darhol qayta urinish uni yanada ko'proq yuklaydi. Kutish vaqtini oshirish serverga tiklanish imkonini beradi.
Ko'p mijoz bir vaqtda qayta urinsa, ular birgalikda yana hujum qiladi. Buning oldini olish uchun tasodifiy qo'shimcha ("jitter") qo'shiladi:
const kechikish = asos * 2 ** i + Math.random() * 100;
Diqqat: hamma xatoni qayta urinmang. 404 yoki 401
qayta urinishdan tuzalmaydi - faqat tarmoq xatosi va 5xx
uchun mantiqiy.
new Promisebilan kechikish funksiyasini yozing.thenzanjiridareturnunutilsa nima bo'lishini ko'ring.asyncfunksiya Promise qaytarishini isbotlang.try/catchbilan asinxron xatoni ushlang.- Ketma-ket va parallel bajarish vaqtini o'lchang.
- Sikl ichidagi
awaitniPromise.allga aylantiring. allvaallSettledfarqini xatoli promise bilan ko'rsating.racebilan vaqt chegarasi qo'ying.- Ushlanmagan rad etishni global tutqich bilan ushlang.
- Eksponensial kechikishli qayta urinish yozing.
Xulosa #
- Promise uch holatda: pending → fulfilled yoki rejected.
- Holat bir marta o'zgaradi va qaytmaydi.
thenichidareturnqilishni unutmang.asyncfunksiya har doim Promise qaytaradi.awaitbilantry/catchasinxron kodda ishlaydi.- Sikl ichida
await- eng ko'p uchraydigan sekinlik sababi. Promise.allparallel bajaradi va tartibni saqlaydi.allbittasi yiqilsa yiqiladi,allSettledhech qachon.racebirinchi tugaganini,anybirinchi muvaffaqiyatni.- Har bir Promise ushlanishi kerak -
unhandledRejection. - Qayta urinishda kechikishni oshiring (backoff + jitter).
Keyingi bo'limda fetch orqali server bilan gaplashamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.